From 871ecbbb88c0b19e01ff80c7c520673e18c31d3e Mon Sep 17 00:00:00 2001 From: Taher Furniturewala Date: Wed, 31 Jul 2024 19:40:16 -0500 Subject: [PATCH 1/3] Applied the diff.patch created from staging to main --- .../.markdownlint-cli2.jsonc | 3 +- .cspell.json | 71 +- .editorconfig | 1 + .eslintrc.js | 7 +- .github/workflows/ci.yml | 4 +- .github/workflows/codeql-analysis.yml | 8 +- .github/workflows/container.yml | 4 +- .github/workflows/stale.yml | 2 +- .gitignore | 2 + .prettierrc.json | 1 + .secrets.env.example | 1 - .vscode/launch.json | 152 +- Dockerfile | 67 +- api/client/banner.ts | 8 +- api/client/context/administration/app.ts | 74 + .../context/administration/appInstallation.ts | 59 + api/client/context/administration/apps.ts | 128 + api/client/context/administration/index.ts | 25 +- .../administration/organization/index.ts | 9 +- .../administration/organization/settings.ts | 123 +- api/client/context/administration/types.ts | 44 + api/client/context/approvals.ts | 16 +- api/client/context/index.ts | 23 +- api/client/context/organization/index.ts | 54 +- api/client/context/organization/repo.ts | 8 +- .../context/organization/repoForkUnlock.ts | 22 +- api/client/context/organization/repos.ts | 21 +- api/client/context/organization/team.ts | 88 +- api/client/context/organization/teams.ts | 7 +- api/client/context/orgs.ts | 4 +- api/client/context/repos.ts | 7 +- api/client/context/sample.ts | 10 +- api/client/context/settings.ts | 48 + api/client/context/teams.ts | 7 +- api/client/index.ts | 20 +- api/client/linking.ts | 14 +- api/client/newOrgRepo.ts | 43 +- api/client/newRepo.ts | 6 +- api/client/news.ts | 8 +- api/client/organization/annotations.ts | 70 +- api/client/organization/index.ts | 34 +- api/client/organization/newRepoMetadata.ts | 8 +- api/client/organization/people.ts | 8 +- api/client/organization/repo.ts | 140 +- api/client/organization/repoPermissions.ts | 6 +- api/client/organization/repos.ts | 29 +- api/client/organization/team.ts | 33 +- api/client/organization/teams.ts | 16 +- api/client/organizations.ts | 161 +- api/client/people.ts | 24 +- api/client/peopleSearch.ts | 2 +- api/client/person.ts | 16 +- api/client/repos.ts | 8 +- api/client/session.ts | 6 +- api/client/teams.ts | 8 +- api/client/users.ts | 35 + api/createRepo.ts | 20 +- api/extension.ts | 30 +- api/index.ts | 24 +- api/jsonErrorHandler.ts | 5 +- api/people/index.ts | 2 +- api/people/link.ts | 6 +- api/people/links.ts | 67 +- api/people/unlink.ts | 12 +- api/webhook.ts | 8 +- app.ts | 156 - bin/www.ts | 6 +- business/account.ts | 48 +- business/application.ts | 42 +- business/collaborator.ts | 26 +- business/domains.ts | 27 +- business/enterprise.ts | 194 + .../auditLogRecord/auditLogRecord.ts | 16 +- .../auditLogRecord/auditLogRecordProvider.ts | 2 +- .../entities}/auditLogRecord/index.ts | 0 .../entities}/auditLogRecord/type.ts | 2 +- .../entities}/localExtensionKey/index.ts | 2 +- .../localExtensionKey/localExtensionKey.ts | 12 +- .../localExtensionKeyProvider.ts | 4 +- .../entities}/organizationAnnotation.ts | 24 +- .../organizationMemberCache/index.ts | 0 .../organizationMemberCache.ts | 20 +- .../organizationMemberCacheProvider.ts | 2 +- .../entities}/organizationSettings/index.ts | 0 .../organizationSetting.ts | 54 +- .../organizationSettingProvider.ts | 2 +- .../entities}/organizationSettings/type.ts | 2 +- {entities => business/entities}/repository.ts | 27 +- .../entities}/repositoryCache/index.ts | 0 .../repositoryCache/repositoryCache.ts | 18 +- .../repositoryCacheProvider.ts | 2 +- .../repositoryCollaboratorCache/index.ts | 0 .../repositoryCollaboratorCache.ts | 18 +- .../repositoryCollaboratorCacheProvider.ts | 2 +- .../entities}/repositoryMetadata/index.ts | 0 .../repositoryMetadata/repositoryMetadata.ts | 16 +- .../repositoryMetadataProvider.ts | 2 +- .../entities}/repositoryMetadata/type.ts | 2 +- .../entities}/repositoryTeamCache/index.ts | 0 .../repositoryTeamCache.ts | 18 +- .../repositoryTeamCacheProvider.ts | 2 +- .../entities}/teamCache/index.ts | 2 +- .../entities}/teamCache/teamCache.ts | 16 +- .../entities}/teamCache/teamCacheProvider.ts | 2 +- .../teamJoinApproval/approvalProvider.ts | 2 +- .../entities}/teamJoinApproval/index.ts | 7 +- .../teamJoinApproval/teamJoinApproval.ts | 16 +- .../teamJoinApprovalProvider.ts | 2 +- .../entities}/teamMemberCache/index.ts | 7 +- .../teamMemberCache/teamMemberCache.ts | 18 +- .../teamMemberCacheProvider.ts | 2 +- .../entities}/token/index.ts | 2 +- .../entities}/token/token.ts | 32 +- .../entities}/token/tokenGenerator.ts | 0 .../entities}/token/tokenProvider.ts | 4 +- {entities => business/entities}/token/type.ts | 2 +- .../entities}/userSettings.ts | 13 +- {features => business/features}/index.ts | 0 .../newRepositories/actions/createReadme.ts | 4 +- .../newRepositories/actions/deleteFork.ts | 2 +- .../actions/downgradeCollaborator.ts | 4 +- .../actions/dropCollaborator.ts | 2 +- .../newRepositories/actions/dropTeam.ts | 2 +- .../newRepositories/actions/index.ts | 0 .../newRepositories/actions/lockdown.ts | 12 +- .../features}/newRepositories/approve.ts | 6 +- .../newRepositories/initializeMetadata.ts | 21 +- .../features}/newRepositories/interfaces.ts | 12 +- .../features}/newRepositories/lockdownMail.ts | 8 +- .../newRepositories/newRepositoryLockdown.ts | 68 +- .../newRepositories/selfServiceDelete.ts | 6 +- .../newRepositories/staticFunctions.ts | 4 +- .../features}/newRepositories/strings.ts | 0 .../validateSelfServiceDelete.ts | 8 +- .../validateSelfServiceSetup.ts | 4 +- .../features}/publicReposFastFilter.ts | 4 +- {features => business/features}/sudo/index.ts | 11 +- {features => business/features}/sudo/noop.ts | 9 +- .../features}/sudo/portal.ts | 8 +- .../features}/sudo/securityGroup.ts | 6 +- {features => business/features}/sudo/sudo.md | 9 +- {features => business/features}/sudo/teams.ts | 6 +- .../teamMemberToMaintainerUpgrade.ts | 14 +- business/githubApps/index.ts | 151 - business/graphManager.ts | 2 +- business/operations/administration.ts | 2 +- business/operations/core.ts | 99 +- business/operations/index.ts | 328 +- business/operations/link.ts | 2 +- business/operations/unlinkMail.ts | 4 +- business/organization.ts | 301 +- business/organizationCopilot.ts | 126 + business/organizationProperties.ts | 198 + business/project.ts | 27 +- business/projectView.ts | 27 +- business/projects.ts | 27 +- business/queryCache.ts | 36 +- business/repoSearch.ts | 6 +- business/repository.ts | 243 +- business/repositoryActions.ts | 21 +- business/repositoryInvitation.ts | 77 + business/repositoryIssue.ts | 21 +- business/repositoryPermission.ts | 83 +- business/repositoryProject.ts | 22 +- business/repositoryProjectCard.ts | 19 +- business/repositoryProjectColumn.ts | 16 +- business/repositoryProperties.ts | 77 + business/repositoryPullRequest.ts | 21 +- business/team.ts | 42 +- business/teamMember.ts | 8 +- business/teamPermission.ts | 70 +- business/teamRepositoryPermission.ts | 47 +- business/user/aggregate.ts | 8 +- business/user/index.ts | 4 +- .../webhooks}/organizationProcessor.ts | 24 +- .../webhooks}/tasks/auditLog.ts | 5 +- .../webhooks}/tasks/automaticTeams.ts | 19 +- .../webhooks}/tasks/index.ts | 0 .../webhooks}/tasks/member.ts | 9 +- .../webhooks}/tasks/membership.ts | 4 +- .../webhooks}/tasks/organization.ts | 4 +- .../webhooks}/tasks/repository.ts | 14 +- {webhooks => business/webhooks}/tasks/team.ts | 24 +- config/brand.types.ts | 1 - config/continuousDeployment.types.ts | 3 + config/debug.json | 4 +- config/debug.types.ts | 2 + config/github.operations.json | 1 - config/github.organizations.types.ts | 1 + config/npm.publishing.json | 6 - config/npm.publishing.types.ts | 17 - config/npm.types.ts | 4 +- config/webServer.json | 13 +- config/webServer.types.ts | 11 + pg.sql => data/pg.sql | 0 default-assets-package/package-lock.json | 1144 +- default-assets-package/package.json | 2 +- docker-compose.yml | 2 +- docs/api.md | 10 +- docs/configuration.md | 8 +- docs/jobs.md | 20 - docs/scripts.md | 5 - docs/service-dependencies.md | 26 + features/graphTeamSync.ts | 31 - index.ts | 97 + interfaces/app.ts | 51 +- interfaces/companySpecific/administration.ts | 2 +- interfaces/companySpecific/events/index.ts | 12 + interfaces/companySpecific/events/linking.ts | 11 + .../features/augmentApiMetadata.ts | 17 +- .../companySpecific/features/firehose.ts | 5 +- interfaces/companySpecific/features/index.ts | 18 +- .../features/organizationSudo.ts | 2 +- .../companySpecific/features/portalSudo.ts | 2 +- interfaces/companySpecific/index.ts | 1 + interfaces/companySpecific/middleware.ts | 10 +- interfaces/companySpecific/passport.ts | 2 +- .../companySpecific/routes/api/index.ts | 1 + interfaces/companySpecific/routes/index.ts | 2 +- interfaces/github/account.ts | 1 + interfaces/github/collaborators.ts | 5 +- interfaces/github/operations.ts | 35 +- interfaces/github/orgs.ts | 25 +- interfaces/github/repos.ts | 162 +- interfaces/github/rest.ts | 23 +- interfaces/github/teams.ts | 23 + interfaces/index.ts | 17 +- interfaces/middleware.ts | 11 + interfaces/providers.ts | 34 +- interfaces/queryCache.ts | 10 +- interfaces/web.ts | 8 +- job.ts | 182 + .../task.ts => cleanupBlobCache.ts} | 11 +- jobs/cleanupBlobCache/index.ts | 9 - .../task.ts => cleanupInvites.ts} | 45 +- jobs/cleanupInvites/index.ts | 13 - jobs/cleanupKeys/index.ts | 9 - jobs/cleanupKeys/task.ts | 98 - jobs/cleanupTeamRequests.ts | 65 + jobs/cleanupTokens/index.ts | 9 - jobs/cleanupTokens/task.ts | 142 - ...itories.ts => deletedRepositoriesCache.ts} | 25 +- jobs/{firehose/task.ts => firehose.ts} | 54 +- jobs/firehose/index.ts | 13 - jobs/permissions.ts | 214 + jobs/permissions/index.ts | 14 - jobs/permissions/task.ts | 155 - .../task.ts => refreshQueryCache.ts} | 30 +- jobs/refreshQueryCache/index.ts | 15 - .../index.ts => refreshUsernames.ts} | 81 +- jobs/repositories.ts | 339 +- lib/caching/blob.ts | 2 +- lib/caching/cosmosdb.ts | 6 +- lib/caching/redis.ts | 4 +- lib/campaigns.ts | 7 +- lib/config/painlessConfigAsCode.ts | 13 +- lib/cosmosSession/index.ts | 2 +- .../entityMetadataProvider.ts | 6 +- lib/entityMetadataProvider/postgres.ts | 66 +- lib/entityMetadataProvider/query.ts | 2 +- lib/entityMetadataProvider/table.ts | 2 +- lib/github/appPurposes.ts | 277 + .../githubApps => lib/github}/appTokens.ts | 59 +- lib/github/collections.ts | 295 +- lib/github/composite.ts | 36 +- lib/github/core.ts | 107 +- lib/github/crossOrganization.ts | 32 +- lib/github/index.ts | 107 +- lib/github/restApi.ts | 84 +- .../githubApps => lib/github}/tokenManager.ts | 210 +- lib/graphProvider/microsoftGraphProvider.ts | 190 +- lib/linkProviders/table/tableLinkProvider.ts | 2 +- lib/mailProvider/index.ts | 43 + lib/pugViewServices.ts | 2 +- transitional.ts => lib/transitional.ts | 107 +- utils.ts => lib/utils.ts | 36 +- middleware/alternateApps.ts | 6 +- middleware/apiAad.ts | 46 +- middleware/apiReposAuth.ts | 7 +- middleware/apiVstsAuth.ts | 7 +- middleware/appInsights.ts | 26 +- middleware/business/administration.ts | 13 +- middleware/business/allLinks.ts | 8 +- middleware/business/authentication.ts | 28 +- .../business/corporateAdministrators.ts | 18 +- middleware/business/corporateAlias.ts | 2 +- middleware/business/corporateHierarchy.ts | 2 +- middleware/business/corporateMail.ts | 25 +- middleware/business/employeesOnly.ts | 8 +- .../{links/index.ts => business/links.ts} | 16 +- middleware/business/organization.ts | 45 +- middleware/business/repository.ts | 34 + middleware/business/setContext.ts | 8 +- middleware/business/userSettings.ts | 41 + middleware/campaign.ts | 6 +- middleware/codespaces.ts | 4 +- middleware/companySpecificDeployment.ts | 6 +- middleware/corporateViews.ts | 2 +- middleware/correlationId.ts | 7 +- middleware/errorHandler.ts | 36 +- .../{error-routes.ts => errorRoutes.ts} | 15 +- .../github/blockEnterpriseManagedUsers.ts | 10 +- .../github/ensureOrganizationProfile.ts | 57 + middleware/github/orgPermissions.ts | 10 +- middleware/github/repoPermissions.ts | 51 +- middleware/github/requireActiveSession.ts | 7 +- middleware/github/systemWidePermissions.ts | 8 +- middleware/github/teamPermissions.ts | 14 +- middleware/healthCheck.ts | 25 +- middleware/index.ts | 22 +- middleware/initialize.ts | 236 +- middleware/jsonError.ts | 11 +- middleware/keyVault.ts | 2 +- middleware/locals.ts | 7 +- middleware/logger.ts | 2 +- middleware/lowercaser.ts | 6 +- middleware/officeHyperlinks.ts | 5 +- middleware/onboarding.ts | 4 +- middleware/passport-routes.ts | 9 +- middleware/passport/aadRoutes.ts | 12 +- middleware/passport/aadStrategy.ts | 83 +- middleware/passport/encryptionSerializer.ts | 2 +- middleware/passport/githubRoutes.ts | 13 +- middleware/passport/githubStrategy.ts | 27 +- .../{passport-config.ts => passportConfig.ts} | 4 +- middleware/rawBodyParser.ts | 5 +- middleware/react.ts | 44 +- middleware/scrubbedUrl.ts | 7 +- middleware/sslify.ts | 1 + middleware/staticClientApp.ts | 5 +- middleware/staticClientApp2.ts | 9 +- middleware/staticSiteAssets.ts | 2 +- middleware/supportMultipleAuthProviders.ts | 6 +- .../index.ts => middleware/types.ts | 9 +- package-lock.json | 17896 +++++++--------- package.json | 139 +- routes/administration/app.ts | 54 +- routes/administration/apps.ts | 57 +- routes/administration/index.ts | 8 +- routes/approvals.ts | 2 +- routes/diagnostics.ts | 14 +- routes/explore.ts | 4 +- routes/index-authenticated.ts | 56 +- routes/index-linked.ts | 10 +- routes/index.ts | 4 +- routes/link-cleanup.ts | 6 +- routes/link.ts | 18 +- routes/org/2fa.ts | 6 +- routes/org/index.ts | 21 +- routes/org/join.ts | 9 +- routes/org/leave.ts | 10 +- routes/org/membership.ts | 8 +- routes/org/newRepoSpa.ts | 6 +- routes/org/people.ts | 4 +- routes/org/profileReview.ts | 6 +- routes/org/repoAdministrativeLock.ts | 19 +- routes/org/repoWorkflowEngine.ts | 18 +- routes/org/repos.ts | 249 +- routes/org/team/approval/index.ts | 6 +- routes/org/team/approvals.ts | 14 +- routes/org/team/delete.ts | 4 +- routes/org/team/index-maintainer.ts | 6 +- routes/org/team/index.ts | 123 +- routes/org/team/leave.ts | 4 +- routes/org/team/maintainers.ts | 12 +- routes/org/team/members.ts | 16 +- routes/org/team/properties.ts | 90 +- routes/org/team/teamAdminRequired.ts | 4 +- routes/org/teams.ts | 10 +- routes/orgAdmin.ts | 62 +- routes/orgs.ts | 12 +- routes/people.ts | 8 +- routes/peopleSearch.ts | 6 +- routes/placeholders.ts | 4 +- routes/releasesSpa.ts | 2 +- routes/repos.ts | 4 +- routes/reposPager.ts | 6 +- routes/settings/approvals.ts | 16 +- routes/settings/authorizations.ts | 8 +- routes/settings/campaigns.ts | 12 +- routes/settings/contributionData.ts | 44 +- routes/settings/index.ts | 2 +- routes/settings/personalAccessTokens.ts | 16 +- routes/teams.ts | 4 +- routes/teamsPager.ts | 2 +- routes/thanks.ts | 2 +- routes/undo.ts | 16 +- routes/unlink.ts | 12 +- scripts/configuration.ts | 28 +- scripts/localEnvironment.ts | 24 +- .../{migrateLinks/task.ts => migrateLinks.ts} | 24 +- scripts/postgres/setup.ts | 2 +- jest.config.ts => test/jest.config.ts | 5 + tsconfig.json | 11 +- views/contributions/eligible.pug | 4 +- views/contributions/popular.pug | 2 - views/contributions/voting/elections.pug | 36 +- views/contributions/voting/vote.pug | 2 - views/email/fossfund-vote.pug | 17 +- views/includes/corporateRepoMetadata.pug | 1 - views/message.pug | 3 + views/nav.pug | 8 +- views/org/pending.pug | 15 +- views/org/team/index.pug | 17 +- views/organization/whois/result.pug | 6 + views/people/index.pug | 2 +- views/repos/defaultBranch.pug | 88 - views/repos/delete.pug | 5 - views/repos/history.pug | 3 - views/repos/index.pug | 11 +- views/repos/permissions.pug | 2 - views/repos/pills.pug | 1 - views/repos/repo.pug | 33 +- views/teams/index.pug | 1 - 414 files changed, 15256 insertions(+), 15795 deletions(-) rename .markdownlint-cli2.jsonc => .config/.markdownlint-cli2.jsonc (59%) create mode 100644 api/client/context/administration/app.ts create mode 100644 api/client/context/administration/appInstallation.ts create mode 100644 api/client/context/administration/apps.ts create mode 100644 api/client/context/administration/types.ts create mode 100644 api/client/context/settings.ts create mode 100644 api/client/users.ts delete mode 100644 app.ts create mode 100644 business/enterprise.ts rename {entities => business/entities}/auditLogRecord/auditLogRecord.ts (94%) rename {entities => business/entities}/auditLogRecord/auditLogRecordProvider.ts (98%) rename {entities => business/entities}/auditLogRecord/index.ts (100%) rename {entities => business/entities}/auditLogRecord/type.ts (69%) rename {entities => business/entities}/localExtensionKey/index.ts (91%) rename {entities => business/entities}/localExtensionKey/localExtensionKey.ts (90%) rename {entities => business/entities}/localExtensionKey/localExtensionKeyProvider.ts (94%) rename {entities => business/entities}/organizationAnnotation.ts (90%) rename {entities => business/entities}/organizationMemberCache/index.ts (100%) rename {entities => business/entities}/organizationMemberCache/organizationMemberCache.ts (93%) rename {entities => business/entities}/organizationMemberCache/organizationMemberCacheProvider.ts (98%) rename {entities => business/entities}/organizationSettings/index.ts (100%) rename {entities => business/entities}/organizationSettings/organizationSetting.ts (89%) rename {entities => business/entities}/organizationSettings/organizationSettingProvider.ts (97%) rename {entities => business/entities}/organizationSettings/type.ts (69%) rename {entities => business/entities}/repository.ts (92%) rename {entities => business/entities}/repositoryCache/index.ts (100%) rename {entities => business/entities}/repositoryCache/repositoryCache.ts (91%) rename {entities => business/entities}/repositoryCache/repositoryCacheProvider.ts (98%) rename {entities => business/entities}/repositoryCollaboratorCache/index.ts (100%) rename {entities => business/entities}/repositoryCollaboratorCache/repositoryCollaboratorCache.ts (94%) rename {entities => business/entities}/repositoryCollaboratorCache/repositoryCollaboratorCacheProvider.ts (99%) rename {entities => business/entities}/repositoryMetadata/index.ts (100%) rename {entities => business/entities}/repositoryMetadata/repositoryMetadata.ts (95%) rename {entities => business/entities}/repositoryMetadata/repositoryMetadataProvider.ts (98%) rename {entities => business/entities}/repositoryMetadata/type.ts (68%) rename {entities => business/entities}/repositoryTeamCache/index.ts (100%) rename {entities => business/entities}/repositoryTeamCache/repositoryTeamCache.ts (93%) rename {entities => business/entities}/repositoryTeamCache/repositoryTeamCacheProvider.ts (98%) rename {entities => business/entities}/teamCache/index.ts (97%) rename {entities => business/entities}/teamCache/teamCache.ts (89%) rename {entities => business/entities}/teamCache/teamCacheProvider.ts (98%) rename {entities => business/entities}/teamJoinApproval/approvalProvider.ts (90%) rename {entities => business/entities}/teamJoinApproval/index.ts (63%) rename {entities => business/entities}/teamJoinApproval/teamJoinApproval.ts (96%) rename {entities => business/entities}/teamJoinApproval/teamJoinApprovalProvider.ts (98%) rename {entities => business/entities}/teamMemberCache/index.ts (95%) rename {entities => business/entities}/teamMemberCache/teamMemberCache.ts (91%) rename {entities => business/entities}/teamMemberCache/teamMemberCacheProvider.ts (99%) rename {entities => business/entities}/token/index.ts (90%) rename {entities => business/entities}/token/token.ts (92%) rename {entities => business/entities}/token/tokenGenerator.ts (100%) rename {entities => business/entities}/token/tokenProvider.ts (95%) rename {entities => business/entities}/token/type.ts (68%) rename {entities => business/entities}/userSettings.ts (92%) rename {features => business/features}/index.ts (100%) rename {features => business/features}/newRepositories/actions/createReadme.ts (93%) rename {features => business/features}/newRepositories/actions/deleteFork.ts (93%) rename {features => business/features}/newRepositories/actions/downgradeCollaborator.ts (87%) rename {features => business/features}/newRepositories/actions/dropCollaborator.ts (93%) rename {features => business/features}/newRepositories/actions/dropTeam.ts (93%) rename {features => business/features}/newRepositories/actions/index.ts (100%) rename {features => business/features}/newRepositories/actions/lockdown.ts (84%) rename {features => business/features}/newRepositories/approve.ts (97%) rename {features => business/features}/newRepositories/initializeMetadata.ts (84%) rename {features => business/features}/newRepositories/interfaces.ts (86%) rename {features => business/features}/newRepositories/lockdownMail.ts (97%) rename {features => business/features}/newRepositories/newRepositoryLockdown.ts (85%) rename {features => business/features}/newRepositories/selfServiceDelete.ts (97%) rename {features => business/features}/newRepositories/staticFunctions.ts (90%) rename {features => business/features}/newRepositories/strings.ts (100%) rename {features => business/features}/newRepositories/validateSelfServiceDelete.ts (90%) rename {features => business/features}/newRepositories/validateSelfServiceSetup.ts (94%) rename {features => business/features}/publicReposFastFilter.ts (94%) rename {features => business/features}/sudo/index.ts (90%) rename {features => business/features}/sudo/noop.ts (65%) rename {features => business/features}/sudo/portal.ts (94%) rename {features => business/features}/sudo/securityGroup.ts (93%) rename {features => business/features}/sudo/sudo.md (90%) rename {features => business/features}/sudo/teams.ts (91%) rename {features => business/features}/teamMemberToMaintainerUpgrade.ts (96%) delete mode 100644 business/githubApps/index.ts create mode 100644 business/organizationCopilot.ts create mode 100644 business/organizationProperties.ts create mode 100644 business/repositoryInvitation.ts create mode 100644 business/repositoryProperties.ts rename {webhooks => business/webhooks}/organizationProcessor.ts (88%) rename {webhooks => business/webhooks}/tasks/auditLog.ts (97%) rename {webhooks => business/webhooks}/tasks/automaticTeams.ts (95%) rename {webhooks => business/webhooks}/tasks/index.ts (100%) rename {webhooks => business/webhooks}/tasks/member.ts (92%) rename {webhooks => business/webhooks}/tasks/membership.ts (96%) rename {webhooks => business/webhooks}/tasks/organization.ts (98%) rename {webhooks => business/webhooks}/tasks/repository.ts (93%) rename {webhooks => business/webhooks}/tasks/team.ts (89%) delete mode 100644 config/npm.publishing.json delete mode 100644 config/npm.publishing.types.ts rename pg.sql => data/pg.sql (100%) delete mode 100644 docs/jobs.md delete mode 100644 docs/scripts.md create mode 100644 docs/service-dependencies.md delete mode 100644 features/graphTeamSync.ts create mode 100644 index.ts create mode 100644 interfaces/companySpecific/events/index.ts create mode 100644 interfaces/companySpecific/events/linking.ts create mode 100644 interfaces/middleware.ts create mode 100644 job.ts rename jobs/{cleanupBlobCache/task.ts => cleanupBlobCache.ts} (81%) delete mode 100644 jobs/cleanupBlobCache/index.ts rename jobs/{cleanupInvites/task.ts => cleanupInvites.ts} (71%) delete mode 100644 jobs/cleanupInvites/index.ts delete mode 100644 jobs/cleanupKeys/index.ts delete mode 100644 jobs/cleanupKeys/task.ts create mode 100644 jobs/cleanupTeamRequests.ts delete mode 100644 jobs/cleanupTokens/index.ts delete mode 100644 jobs/cleanupTokens/task.ts rename jobs/{refreshQueryCache/deletedRepositories.ts => deletedRepositoriesCache.ts} (91%) rename jobs/{firehose/task.ts => firehose.ts} (89%) delete mode 100644 jobs/firehose/index.ts create mode 100644 jobs/permissions.ts delete mode 100644 jobs/permissions/index.ts delete mode 100644 jobs/permissions/task.ts rename jobs/{refreshQueryCache/task.ts => refreshQueryCache.ts} (97%) delete mode 100644 jobs/refreshQueryCache/index.ts rename jobs/{refreshUsernames/index.ts => refreshUsernames.ts} (67%) create mode 100644 lib/github/appPurposes.ts rename {business/githubApps => lib/github}/appTokens.ts (76%) rename {business/githubApps => lib/github}/tokenManager.ts (65%) rename transitional.ts => lib/transitional.ts (75%) rename utils.ts => lib/utils.ts (90%) rename middleware/{links/index.ts => business/links.ts} (90%) create mode 100644 middleware/business/repository.ts create mode 100644 middleware/business/userSettings.ts rename middleware/{error-routes.ts => errorRoutes.ts} (74%) create mode 100644 middleware/github/ensureOrganizationProfile.ts rename middleware/{passport-config.ts => passportConfig.ts} (96%) rename scripts/migrateLinks/index.ts => middleware/types.ts (50%) rename scripts/{migrateLinks/task.ts => migrateLinks.ts} (84%) rename jest.config.ts => test/jest.config.ts (66%) delete mode 100644 views/repos/defaultBranch.pug diff --git a/.markdownlint-cli2.jsonc b/.config/.markdownlint-cli2.jsonc similarity index 59% rename from .markdownlint-cli2.jsonc rename to .config/.markdownlint-cli2.jsonc index 7c3b70235..92bfbc2e7 100644 --- a/.markdownlint-cli2.jsonc +++ b/.config/.markdownlint-cli2.jsonc @@ -5,7 +5,8 @@ "MD024": { "allow_different_nesting": true }, - "MD026": false + "MD026": false, + "MD033": false // no inline HTML: we use inline HTML for expandable details sections }, "ignores": ["**/node_modules/**"] } diff --git a/.cspell.json b/.cspell.json index 22e5b16fe..83a11efe1 100644 --- a/.cspell.json +++ b/.cspell.json @@ -5,6 +5,7 @@ ".git/", ".environment/**", "**/jitStaticConfiguration.json", + "**/*Accounts.json", "**/sentiment/**", "**/thirdparty/**", "**/vendor/**", @@ -13,22 +14,26 @@ "default-assets-package/resources/repos-css/oss.css", "**/commonKnownExternalCollaborators.json", "jobs/reports/exemptRepositories.json", - "**/hotfixRepos.json", "**/demo/team.json", "**/demo/user.json", "**/*_html.ts", "**/*.ignore_data.ts", "package*.json", - ".devcontainer/nih/**" + ".devcontainer/nih/**", + "**/assets/*.xlsx", + "**/known.csv" ], "enableGlobDot": true, "useGitignore": true, "words": [ + "1es", + "1escopilot", "aadgraph", "aadid", "aadname", "aadoid", "aadupn", + "accesslevel", "accountinfo", "accountinformation", "actionperms", @@ -39,6 +44,7 @@ "actionsplatforms", "actionsupdated", "actionsused", + "actionsworkflowcount", "actorid", "adal", "additionals", @@ -56,6 +62,7 @@ "agreementversion", "alladmins", "allowunlock", + "alreadyrestored", "aosp", "apikey", "APPDIR", @@ -89,6 +96,7 @@ "blockui", "bootswatch", "bootup", + "branchname", "breakingchangereviewrequired", "browserconfig", "builddir", @@ -107,12 +115,17 @@ "CELA", "champscount", "champstatus", + "checkmarks", + "chromedriver", + "citus", + "citusdata", "classificationdelegated", "classificationnative", "classificationnativechecked", "classificationupdated", "classificationupdatedby", "classname", + "classpath", "cleanupblobs", "cleanupinvites", "cleanupkeys", @@ -134,6 +147,7 @@ "codespace", "codespaces", "collab", + "collaboratorid", "collabs", "columnbreak", "commitcomment", @@ -152,8 +166,10 @@ "contributionshareoptin", "copilotaccess", "copilotagreement", + "copilotprod", "copilotrequest", "Copybara", + "corpnet", "corporatealias", "corporatecount", "corporatedev", @@ -164,6 +180,7 @@ "corporatename", "corporateprod", "corporateusername", + "countuse", "createdat", "creategithubaccount", "creds", @@ -179,12 +196,14 @@ "DATETIME", "dbaeumer", "dcount", + "deadcode", "decisionmaker", "Decisionmaker", "Decisionmakers", "deepmerge", "defaultbranch", "deferreason", + "definitelytyped", "deislabs", "deletedetected", "deletesettingsorgname", @@ -199,6 +218,8 @@ "devstaging", "directoryname", "directowners", + "disablewave", + "distributeonly", "distro", "divisionid", "dnfmit", @@ -211,6 +232,7 @@ "dropup", "DSRE", "eastus", + "Ecosyste", "edgecase", "electionid", "eligibilityend", @@ -245,6 +267,7 @@ "enterprisevisited", "entityid", "entitytype", + "entra", "esbody", "esemail", "eshead", @@ -252,6 +275,7 @@ "eventid", "eventrecord", "eventtype", + "exceljs", "existingrepoid", "existingreponame", "fakeaccesstoken", @@ -263,9 +287,11 @@ "firstconfigured", "FIRSTRUNFILE", "flipgrid", + "fontawesome", "fontname", "forceopen", "forkscount", + "fortawesome", "FOSS", "FOSSFUND", "fossfundabout", @@ -287,8 +313,8 @@ "GHID", "GHMP", "ghname", - "ghowners", "ghossapis", + "ghowners", "GHPI", "ghpimigrationphase", "ghrp", @@ -323,9 +349,12 @@ "haswiki", "hierarchynames", "hierarchytext", + "highcharts", "hsts", "Hsts", "HSTS", + "hubber", + "hubbers", "hubot", "IAAD", "ictext", @@ -363,15 +392,18 @@ "issuecomment", "issuesopened", "istest", + "jabberpl", "jeffwilcox", "jitgrants", "joblog", "jobname", + "json", "jsoncontribution", "jsoncreated", "jwilcox", "jwks", "JWTs", + "keybytes", "keyout", "keyvault", "KEYVAULT", @@ -386,6 +418,7 @@ "leftouter", "legalcontact", "legalentities", + "libsodium", "lightup", "linkid", "linkmanager", @@ -448,6 +481,8 @@ "monthlycontributionactivity", "msapplication", "msecnd", + "MSEULA", + "MSFTEULA", "MSFTIES", "msftmetadata", "msrc", @@ -468,6 +503,8 @@ "newrepolockremoved", "nihgithubportal", "nihdevgithubportal", + "Newtonsoft", + "NOASSERTION", "nodeapp", "nodenext", "noemail", @@ -479,8 +516,10 @@ "notifiedissueid", "npminitignore", "npminstallignore", + "npmjs", "npmrc", "nums", + "Nunit", "octicon", "odata", "offboard", @@ -511,6 +550,7 @@ "opensourcedocsprod", "opensourceprod", "opensourcepulls", + "opensourcerepoowners", "opensourcereposdev", "opensourcereposprod", "opensourcesite", @@ -539,6 +579,7 @@ "organizationlogin", "organizationmembercache", "organizationname", + "organizationrunners", "organizationsetting", "organizationsettings", "orgid", @@ -593,6 +634,7 @@ "pmownerid", "popd", "portaldescription", + "portaldev", "portalppe", "portalprod", "portalwestus", @@ -642,6 +684,8 @@ "removeothergithubaccount", "REPLYTO", "repocount", + "repodisablebranchprotections", + "repodisableoutsidecollaborators", "repoid", "repoidifyouhaveit", "repolockdown", @@ -679,6 +723,8 @@ "revokedbycorporateid", "rootdir", "rubygem", + "rubygems", + "RUNNERDATA", "runtimes", "samplescollaboratorusername", "samplescontact", @@ -690,6 +736,8 @@ "scansummary", "SCIM", "scorecardreprocessrequest", + "secretbox", + "secretless", "secretscanningonpush", "secscandata", "securityevents", @@ -726,6 +774,7 @@ "shortcutting", "shouldrestore", "showids", + "shuf", "Sida", "signin", "signoff", @@ -738,13 +787,11 @@ "specialteams", "SSIRP", "sslify", - "Sslify", "standardsredirect", "stargazerscount", "startclean", - "StartClean", + "startofday", "startright", - "StartRight", "startswith", "statechecked", "steelblue", @@ -784,6 +831,8 @@ "timezoneoffset", "tlink", "tolower", + "Toolset", + "toscalar", "totalcount", "touchedtime", "toupper", @@ -803,6 +852,7 @@ "unadopted", "unarchived", "Unauthoritative", + "Uncommited", "unconfigured", "Unconfigured", "unconfirmedorganizationname", @@ -850,21 +900,22 @@ "Wcag", "webapps", "webcontext", + "webfonts", "webhoooks", "welcomeemailsent", "whois", "Whois", "withmaintainers", "withservicetree", - "workboard", - "Workboard", - "workboarding", + "withsource", + "wixtoolset", "xamarinhq", "Xcache", "xlink", "XSTORE", "xtable", - "yamls" + "yamls", + "yml" ], "maxNumberOfProblems": 1000 } diff --git a/.editorconfig b/.editorconfig index d7ce5c432..6375e1c4c 100644 --- a/.editorconfig +++ b/.editorconfig @@ -9,3 +9,4 @@ indent_size = 2 trim_trailing_whitespace = true charset = utf-8 end_of_line = lf +tabCompletion = on diff --git a/.eslintrc.js b/.eslintrc.js index cf1525756..3c5d9899b 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -41,5 +41,10 @@ module.exports = { }, }, ], - ignorePatterns: ['default-assets-package/thirdparty/**/*.js', 'dist/**/*.js', '**/vendor/**'], + ignorePatterns: [ + 'default-assets-package/thirdparty/**/*.js', + 'dist/**/*.js', + 'dist/**/*.d.ts', + '**/vendor/**', + ], }; diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 27cb497a5..44beab153 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,10 +13,10 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Node - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: cache: npm diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index be69f6ca4..2eb6669f6 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -15,11 +15,11 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 # Override language selection by uncommenting this and choosing your languages # with: # languages: go, javascript, csharp, python, cpp, java @@ -27,7 +27,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@v3 # ℹī¸ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -41,4 +41,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/container.yml b/.github/workflows/container.yml index 179b287a5..c1dd30444 100644 --- a/.github/workflows/container.yml +++ b/.github/workflows/container.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout 🛒 - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Packages đŸ“Ļ run: | @@ -36,7 +36,7 @@ jobs: environment: name: development steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 #- uses: docker/build-push-action@v3 - name: Azure OpenID Connect ✨ diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 350edfea4..65f5bbf4e 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -13,7 +13,7 @@ jobs: stale: runs-on: ubuntu-latest steps: - - uses: actions/stale@v8 + - uses: actions/stale@v9 with: stale-issue-message: This issue has been identified as stale because it has gone 30 days with no activity. diff --git a/.gitignore b/.gitignore index d83fe8f0b..d7f45ffdf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ dist/ node_modules/ .DS_Store +reports/ # Potential local development environment scripts app.yaml @@ -13,3 +14,4 @@ feedscan feedscan* ..prettierignore.swp scripts/localCodespace.ts +junit.xml diff --git a/.prettierrc.json b/.prettierrc.json index 32e9fdbd7..f7aa09f41 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -1,4 +1,5 @@ { "singleQuote": true, + "trailingComma": "es5", "printWidth": 110 } diff --git a/.secrets.env.example b/.secrets.env.example index a81c4a1ee..896300ed7 100644 --- a/.secrets.env.example +++ b/.secrets.env.example @@ -1,4 +1,3 @@ -GITHUB_CENTRAL_OPERATIONS_TOKEN= GITHUB_CLIENT_ID= GITHUB_CLIENT_SECRET= GITHUB_CALLBACK_URL=http://localhost:3000/auth/github/callback diff --git a/.vscode/launch.json b/.vscode/launch.json index d1caa8213..491b43343 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -4,7 +4,7 @@ { "type": "node", "request": "launch", - "name": "Launch site 3000", + "name": "Launch site :4000 sudo", "program": "${workspaceFolder}/dist/bin/www.js", "cwd": "${workspaceFolder}/dist", "preLaunchTask": "tsbuild", @@ -12,26 +12,32 @@ "console": "integratedTerminal", "env": { "NODE_ENV": "development", - "DEBUG": "startup,g:server,context,github:tokens", - "MORE_DEBUG": "appinsights,cache,restapi,pg,querycache,user,redis-cross-org,health" + "PORT": "4000", + "DEBUG_GITHUB_PORTAL_SUDO_FORCE": "1", + "DEBUG": "startup,cosmosdb,g:server,context,*simple-oauth2*,appinsights,insights", + "MORE_DEBUG": "appinsights,cache,restapi,pg,querycache,user,redis-cross-org" } }, { "type": "node", "request": "launch", - "name": "Jest Tests", - "program": "${workspaceRoot}/node_modules/jest/bin/jest.js", - "args": ["-i"], + "name": "Launch site :4000 as-is", + "program": "${workspaceFolder}/dist/bin/www.js", + "cwd": "${workspaceFolder}/dist", "preLaunchTask": "tsbuild", "sourceMaps": true, - "internalConsoleOptions": "openOnSessionStart", - "console": "externalTerminal", - "outFiles": ["${workspaceRoot}/dist/**/*"] + "console": "integratedTerminal", + "env": { + "NODE_ENV": "development", + "PORT": "4000", + "DEBUG": "startup,g:server,context,*simple-oauth2*,appinsights,insights,appinsights", + "_DEBUG": "cosmosdb,cache" + } }, { "type": "node", "request": "launch", - "name": "Launch site 4000 sudo", + "name": "Launch site :4000 sudo OFF", "program": "${workspaceFolder}/dist/bin/www.js", "cwd": "${workspaceFolder}/dist", "preLaunchTask": "tsbuild", @@ -40,15 +46,16 @@ "env": { "NODE_ENV": "development", "PORT": "4000", - "DEBUG_GITHUB_PORTAL_SUDO_FORCE": "1", - "DEBUG": "startup,cosmosdb,g:server,context,*simple-oauth2*,appinsights,insights", + "DEBUG": "startup,cosmosdb,g:server,context", + "DEBUG_GITHUB_PORTAL_SUDO_OFF": "1", + "DEBUG_GITHUB_ORG_SUDO_OFF": "1", "MORE_DEBUG": "appinsights,cache,restapi,pg,querycache,user,redis-cross-org" } }, { "type": "node", "request": "launch", - "name": "Launch site 4000 sudo OFF", + "name": "Launch server-rendered legacy site :3000", "program": "${workspaceFolder}/dist/bin/www.js", "cwd": "${workspaceFolder}/dist", "preLaunchTask": "tsbuild", @@ -56,18 +63,15 @@ "console": "integratedTerminal", "env": { "NODE_ENV": "development", - "PORT": "4000", - "DEBUG": "startup,cosmosdb,g:server,context", - "DEBUG_GITHUB_PORTAL_SUDO_OFF": "1", - "DEBUG_GITHUB_ORG_SUDO_OFF": "1", - "MORE_DEBUG": "appinsights,cache,restapi,pg,querycache,user,redis-cross-org" + "DEBUG": "startup,g:server,context", + "MORE_DEBUG": "appinsights,cache,restapi,pg,querycache,user,redis-cross-org,health,github:tokens" } }, { "type": "node", "request": "launch", - "name": "Background Job: Firehose", - "program": "${workspaceRoot}/dist/jobs/firehose/index.js", + "name": "Background Job: Event firehose", + "program": "${workspaceRoot}/dist/jobs/firehose.js", "preLaunchTask": "tsbuild", "sourceMaps": true, "console": "integratedTerminal", @@ -77,6 +81,18 @@ "DEBUG": "startup,querycache" } }, + { + "type": "node", + "request": "launch", + "name": "Jest Tests", + "program": "${workspaceRoot}/node_modules/jest/bin/jest.js", + "args": ["-i"], + "preLaunchTask": "tsbuild", + "sourceMaps": true, + "internalConsoleOptions": "openOnSessionStart", + "console": "externalTerminal", + "outFiles": ["${workspaceRoot}/dist/**/*"] + }, { "type": "node", "request": "launch", @@ -118,7 +134,7 @@ "sourceMaps": true, "env": { "NODE_ENV": "development", - "DEBUG": "appinsights,restapi,startup,redis-cross-org,appinsights", + "DEBUG": "appinsights,startup", "DEBUG_GITHUB_PORTAL_SUDO_OFF": "1", "DEBUG_GITHUB_ORG_SUDO_OFF": "1" } @@ -134,42 +150,30 @@ "console": "integratedTerminal", "env": { "NODE_ENV": "localhost", - "DEBUG": "startup" + "DEBUG": "startup", + "EXIT_IMMEDIATELY": "1" } }, { "type": "node", "request": "launch", - "name": "Job: Cleanup invitations", - "program": "${workspaceRoot}/dist/jobs/cleanupInvites/index.js", - "cwd": "${workspaceRoot}/dist", - "preLaunchTask": "tsbuild", - "sourceMaps": true, - "console": "integratedTerminal", - "env": { - "NODE_ENV": "development", - "DEBUG": "redis,restapi,startup,appinsights" - } - }, - { - "type": "node", - "request": "launch", - "name": "Job: Cleanup blob cache", - "program": "${workspaceRoot}/dist/jobs/cleanupBlobCache/index.js", + "name": "66: Order 66 Terminations", + "program": "${workspaceRoot}/dist/microsoft/jobs/terminations.js", + "args": ["all"], "cwd": "${workspaceRoot}/dist", "preLaunchTask": "tsbuild", "sourceMaps": true, "console": "integratedTerminal", "env": { "NODE_ENV": "development", - "DEBUG": "startup" + "DEBUG": "startup,appinsights" } }, { "type": "node", "request": "launch", "name": "Job: Backfill aliases (3)", - "program": "${workspaceRoot}/dist/jobs/refreshUsernames/index.js", + "program": "${workspaceRoot}/dist/jobs/refreshUsernames.js", "cwd": "${workspaceRoot}/dist", "preLaunchTask": "tsbuild", "sourceMaps": true, @@ -184,21 +188,21 @@ "type": "node", "request": "launch", "name": "Job: User attributes hygiene (4)", - "program": "${workspaceRoot}/dist/jobs/refreshUsernames/index.js", + "program": "${workspaceRoot}/dist/jobs/refreshUsernames.js", "cwd": "${workspaceRoot}/dist", "preLaunchTask": "tsbuild", "sourceMaps": true, "console": "integratedTerminal", "env": { "NODE_ENV": "development", - "DEBUG": "redis,restapi,startup,appinsights,cache" + "DEBUG": "startup,appinsights" } }, { "type": "node", "request": "launch", "name": "Job: Consistency: All (6)", - "program": "${workspaceRoot}/dist/jobs/refreshQueryCache/index.js", + "program": "${workspaceRoot}/dist/jobs/refreshQueryCache.js", "args": ["all"], "cwd": "${workspaceRoot}/dist", "preLaunchTask": "tsbuild", @@ -213,7 +217,7 @@ "type": "node", "request": "launch", "name": "Job: Consistency: Deleted repos (7)", - "program": "${workspaceRoot}/dist/jobs/refreshQueryCache/deletedRepositories.js", + "program": "${workspaceRoot}/dist/jobs/deletedRepositoriesCache.js", "args": [], "cwd": "${workspaceRoot}/dist", "preLaunchTask": "tsbuild", @@ -228,7 +232,7 @@ "type": "node", "request": "launch", "name": "Job: Consistency: Teams (8)", - "program": "${workspaceRoot}/dist/jobs/refreshQueryCache/index.js", + "program": "${workspaceRoot}/dist/jobs/refreshQueryCache.js", "args": ["teams"], "cwd": "${workspaceRoot}/dist", "preLaunchTask": "tsbuild", @@ -243,7 +247,7 @@ "type": "node", "request": "launch", "name": "Job: Consistency: Org members (9)", - "program": "${workspaceRoot}/dist/jobs/refreshQueryCache/index.js", + "program": "${workspaceRoot}/dist/jobs/refreshQueryCache.js", "args": ["organizations"], "cwd": "${workspaceRoot}/dist", "preLaunchTask": "tsbuild", @@ -258,7 +262,7 @@ "type": "node", "request": "launch", "name": "Job: Consistency: Repo collaborators (10)", - "program": "${workspaceRoot}/dist/jobs/refreshQueryCache/index.js", + "program": "${workspaceRoot}/dist/jobs/refreshQueryCache.js", "args": ["collaborators"], "cwd": "${workspaceRoot}/dist", "preLaunchTask": "tsbuild", @@ -273,7 +277,7 @@ "type": "node", "request": "launch", "name": "Job: Consistency: Team permissions (11)", - "program": "${workspaceRoot}/dist/jobs/refreshQueryCache/index.js", + "program": "${workspaceRoot}/dist/jobs/refreshQueryCache.js", "args": ["permissions"], "cwd": "${workspaceRoot}/dist", "preLaunchTask": "tsbuild", @@ -301,8 +305,8 @@ { "type": "node", "request": "launch", - "name": "Job: Migrate links", - "program": "${workspaceRoot}/dist/jobs/migrateLinks/index.js", + "name": "Job: Migrate links (14)", + "program": "${workspaceRoot}/dist/jobs/migrateLinks.js", "preLaunchTask": "tsbuild", "sourceMaps": true, "console": "integratedTerminal", @@ -315,8 +319,8 @@ { "type": "node", "request": "launch", - "name": "Job: System Team Permissions", - "program": "${workspaceRoot}/dist/jobs/permissions/index.js", + "name": "Job: System Team Permissions (15)", + "program": "${workspaceRoot}/dist/jobs/permissions.js", "cwd": "${workspaceRoot}/dist", "preLaunchTask": "tsbuild", "sourceMaps": true, @@ -326,6 +330,48 @@ "DEBUG": "startup,appinsights" } }, + { + "type": "node", + "request": "launch", + "name": "Job: Cleanup invitations (16)", + "program": "${workspaceRoot}/dist/jobs/cleanupInvites.js", + "cwd": "${workspaceRoot}/dist", + "preLaunchTask": "tsbuild", + "sourceMaps": true, + "console": "integratedTerminal", + "env": { + "NODE_ENV": "development", + "DEBUG": "redis,restapi,startup,appinsights" + } + }, + { + "type": "node", + "request": "launch", + "name": "Job: Cleanup blob cache (17)", + "program": "${workspaceRoot}/dist/jobs/cleanupBlobCache.js", + "cwd": "${workspaceRoot}/dist", + "preLaunchTask": "tsbuild", + "sourceMaps": true, + "console": "integratedTerminal", + "env": { + "NODE_ENV": "development", + "DEBUG": "startup" + } + }, + { + "type": "node", + "request": "launch", + "name": "Job: Cleanup team requests (18)", + "program": "${workspaceRoot}/dist/jobs/cleanupTeamRequests.js", + "cwd": "${workspaceRoot}/dist", + "preLaunchTask": "tsbuild", + "sourceMaps": true, + "console": "integratedTerminal", + "env": { + "NODE_ENV": "development", + "DEBUG": "startup" + } + }, { "type": "node", "request": "launch", @@ -345,7 +391,7 @@ "type": "node", "request": "launch", "name": "Script: Import audit CSV", - "program": "${workspaceRoot}/dist/jobs/importAuditLog/index.js", + "program": "${workspaceRoot}/dist/jobs/importAuditLog.js", "preLaunchTask": "tsbuild", "sourceMaps": true, "console": "integratedTerminal", diff --git a/Dockerfile b/Dockerfile index 9daba8526..a3f30f081 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,40 +1,35 @@ -ARG IMAGE_NAME=node:16-alpine +# +# Copyright (c) Microsoft. +# Licensed under the MIT license. See LICENSE file in the project root for full license information. +# + +ARG IMAGE_NAME=mcr.microsoft.com/cbl-mariner/base/nodejs:18 FROM $IMAGE_NAME AS build ARG NPM_TOKEN -# Make Git available for NPM and rsync in the build image -RUN apk add --update git && rm -rf /var/cache/apk/* - -WORKDIR /build -COPY package.json . -COPY package-lock.json . +RUN tdnf -y update --quiet -# Only if needed, copy .npmrc files into the container -# COPY Dockerfile.npmrc /build/.npmrc +# We used to also make Git available for NPM and rsync in build +# tdnf clean all --quiet && \ +# tdnf -y install ca-certificates git --quiet && \ -# If you are doing local development and OK with your private tokens in the contains (CAREFUL): -# DO NOT RECOMMEND: -# COPY .npmrc /build/.npmrc - -# RUN npm install --production --verbose && mv node_modules production_node_modules -RUN npm install --production && mv node_modules production_node_modules +WORKDIR /build COPY . . -# Only if needed, copy .npmrc files into the container, again... -# COPY Dockerfile.npmrc /build/.npmrc +# Only if needed, copy file with NPM_TOKEN arg +# COPY .npmrc.arg /build/.npmrc -# Dev dependencies -# RUN npm install --verbose && rm -rf .npmrc -RUN npm install && rm -rf .npmrc - -# TypeScript build +RUN npm install --ignore-scripts --production --verbose +RUN npm ci RUN npm run-script build +RUN mv node_modules production_node_modules +RUN rm -f .npmrc # The open source project build needs: build the site assets sub-project -RUN cd default-assets-package && npm install && npm run build +RUN cd default-assets-package && npm ci && npm run build FROM $IMAGE_NAME AS run @@ -47,24 +42,26 @@ EXPOSE 3000 WORKDIR /usr/src/repos -RUN addgroup oss && adduser -D -G oss oss && chown -R oss:oss . - # Production Node.js modules -COPY --from=build --chown=oss:oss /build/production_node_modules ./node_modules +COPY --from=build /build/production_node_modules ./node_modules # People not using painless config may need -COPY --from=build --chown=oss:oss /build/data ./data +COPY --from=build /build/data ./data # Copy built assets, app, config map -COPY --from=build --chown=oss:oss /build/dist ./ +COPY --from=build /build/dist ./ # The open source project build needs: default assets should be placed -COPY --from=build --chown=oss:oss /build/default-assets-package ./default-assets-package +COPY --from=build /build/default-assets-package ./default-assets-package + +COPY --from=build /build/config ./config +COPY --from=build /build/views ./views +COPY --from=build /build/package.json ./package.json + +# Only if needed, copy our environment +# COPY --from=build /build/.environment ./.environment -COPY --from=build --chown=oss:oss /build/config ./config -COPY --from=build --chown=oss:oss /build/views ./views -COPY --from=build --chown=oss:oss /build/package.json ./package.json +# Only if needed, binary resources +# COPY --from=build /build/microsoft/assets ./microsoft/assets -# Host the app -USER oss -ENTRYPOINT ["npm", "run-script", "start-in-container"] \ No newline at end of file +ENTRYPOINT ["npm", "run-script", "start-in-container"] diff --git a/api/client/banner.ts b/api/client/banner.ts index 732a5cc68..a4ccd0f88 100644 --- a/api/client/banner.ts +++ b/api/client/banner.ts @@ -3,17 +3,17 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import { ReposAppRequest } from '../../interfaces'; import { jsonError } from '../../middleware'; -import { getProviders } from '../../transitional'; +import { getProviders } from '../../lib/transitional'; const router: Router = Router(); // TODO: move to modern w/administration experience, optionally -router.get('/', (req: ReposAppRequest, res, next) => { +router.get('/', (req: ReposAppRequest, res: Response, next: NextFunction) => { const { config } = getProviders(req); const text = config?.serviceMessage?.banner || null; const link = config.serviceMessage?.link; @@ -22,7 +22,7 @@ router.get('/', (req: ReposAppRequest, res, next) => { return res.json({ banner }); }); -router.use('*', (req, res, next) => { +router.use('*', (req, res: Response, next: NextFunction) => { return next(jsonError('no API or function available within this banner route', 404)); }); diff --git a/api/client/context/administration/app.ts b/api/client/context/administration/app.ts new file mode 100644 index 000000000..0734a4385 --- /dev/null +++ b/api/client/context/administration/app.ts @@ -0,0 +1,74 @@ +// +// Copyright (c) Microsoft. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +import { NextFunction, Response, Router } from 'express'; +import asyncHandler from 'express-async-handler'; + +import { ReposAppRequest } from '../../../../interfaces'; +import { CreateError, getProviders } from '../../../../lib/transitional'; + +import routeIndividualApp from './app'; +import GitHubApplication from '../../../../business/application'; +import { OrganizationSetting } from '../../../../business/entities/organizationSettings/organizationSetting'; +import { sortByCaseInsensitive } from '../../../../lib/utils'; +import routeApplicationInstallation from './appInstallation'; +import { ApiRequestWithGitHubApplication, RequestWithInstallation } from './types'; + +const router: Router = Router(); + +router.get( + '/', + asyncHandler(async function (req: ApiRequestWithGitHubApplication, res: Response, next: NextFunction) { + const { gitHubApplication } = req; + const installationIdString = req.query.installation_id; + const setupAction = req.query.setup_action; + // if (installationIdString && setupAction) { + // return res.redirect( + // `./${githubApplication.id}/installations/${installationIdString}?setup_action=${setupAction}` + // ); + // } + // const individualContext = req.individualContext; + const allInstalls = await gitHubApplication.getInstallations({ maxAgeSeconds: 5 }); + const { valid, invalid } = GitHubApplication.filterInstallations(allInstalls); + return res.json({ + state: { + installations: { + valid, + invalid, + }, + app: gitHubApplication.asClientJson(), + }, + }) as unknown as void; + }) +); + +router.use( + '/installations/:installationId', + asyncHandler(async function (req: RequestWithInstallation, res, next) { + // const installationIdString = req.query.installation_id; + // const setupAction = req.query.setup_action; + const { gitHubApplication } = req; + const { installationId: installationIdAsString } = req.params; + const installationId = Number(installationIdAsString); + const installation = await gitHubApplication.getInstallation(installationId); + if (!installation) { + return next( + CreateError.NotFound( + `The GitHub app installation ${installationIdAsString} could not be found for app ${gitHubApplication.id}` + ) + ); + } + req.installation = installation; + return next(); + }) +); + +router.use('/installations/:installationId', routeApplicationInstallation); + +router.use('*', (req, res: Response, next: NextFunction) => { + return next(CreateError.NotFound('no API or function available: context/administration/apps/...')); +}); + +export default router; diff --git a/api/client/context/administration/appInstallation.ts b/api/client/context/administration/appInstallation.ts new file mode 100644 index 000000000..b05c906a1 --- /dev/null +++ b/api/client/context/administration/appInstallation.ts @@ -0,0 +1,59 @@ +// +// Copyright (c) Microsoft. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +import { NextFunction, Response, Router } from 'express'; +import asyncHandler from 'express-async-handler'; + +import { CreateError, getProviders } from '../../../../lib/transitional'; +import { OrganizationSetting } from '../../../../business/entities/organizationSettings/organizationSetting'; +import { AdministrativeGitHubAppInstallationResponse, RequestWithInstallation } from './types'; + +const router: Router = Router(); + +router.use( + asyncHandler(async function (req: RequestWithInstallation, res: Response, next: NextFunction) { + const { operations, organizationSettingsProvider } = getProviders(req); + + const { installation } = req; + const organizationId = installation.account.id; + const organizationName = installation.account.login; + let settings: OrganizationSetting = null; + try { + settings = await organizationSettingsProvider.getOrganizationSetting(organizationId.toString()); + } catch (notFound) { + /* ignored */ + } + req.organizationDynamicSettings = settings; + + const staticSettings = operations.getOrganizationSettings(organizationName); + req.organizationStaticSettings = staticSettings; + + return next(); + }) +); + +router.get( + '/', + asyncHandler(async (req: RequestWithInstallation, res: Response, next: NextFunction) => { + const { gitHubApplication, installation, organizationDynamicSettings, organizationStaticSettings } = req; + const response: AdministrativeGitHubAppInstallationResponse = { + app: gitHubApplication.asClientJson(), + // installation, + installationId: installation.id, + dynamicSettings: organizationDynamicSettings, + }; + return res.json(response) as unknown as void; + }) +); + +router.use('*', (req, res: Response, next: NextFunction) => { + return next( + CreateError.NotFound( + 'no API or function available: context/administration/apps/:appId/installations/:installationId' + ) + ); +}); + +export default router; diff --git a/api/client/context/administration/apps.ts b/api/client/context/administration/apps.ts new file mode 100644 index 000000000..f9f6e3db0 --- /dev/null +++ b/api/client/context/administration/apps.ts @@ -0,0 +1,128 @@ +// +// Copyright (c) Microsoft. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +import { NextFunction, Response, Router } from 'express'; +import asyncHandler from 'express-async-handler'; + +import { ReposAppRequest } from '../../../../interfaces'; +import { CreateError, getProviders } from '../../../../lib/transitional'; + +import routeIndividualApp from './app'; +import GitHubApplication from '../../../../business/application'; +import { sortByCaseInsensitive } from '../../../../lib/utils'; +import { + ApiRequestWithGitHubApplication, + ManagedOrganizationAppConfigurationsByOrgView, + ManagedOrganizationStatus, +} from './types'; + +const router: Router = Router(); + +router.get( + '/', + asyncHandler(async (req: ReposAppRequest, res: Response) => { + const { operations, organizationSettingsProvider } = getProviders(req); + const apps = operations.getApplications(); + const byOrg = new Map(); + function getOrg(name: string) { + let o = byOrg.get(name); + if (!o) { + o = { + organizationName: name, + id: undefined, + status: ManagedOrganizationStatus.NotAdopted, + appInstallations: new Map(), + dynamicSettings: null, + configuredInstallations: [], + }; + for (const app of apps) { + o.appInstallations.set(app.id, null); + } + byOrg.set(name, o); + } + return o; + } + for (const app of apps) { + const appInstalls = await app.getInstallations({ maxAgeSeconds: 5 }); + const { valid: validInstallations } = GitHubApplication.filterInstallations(appInstalls); + for (const valid of validInstallations) { + const organizationName = valid.account.login; + const o = getOrg(organizationName.toLowerCase()); + o.appInstallations.set(app.id, { + app, + installationId: valid.id, + }); + o.id = Number(valid.target_id); + if (!o.dynamicSettings && valid.target_type === 'Organization') { + try { + o.dynamicSettings = await organizationSettingsProvider.getOrganizationSetting( + valid.target_id.toString() + ); + } catch (ignore) { + /* ignored */ + } + if (o.dynamicSettings) { + o.configuredInstallations = o.dynamicSettings.installations.map( + (install) => install.installationId + ); + o.status = ManagedOrganizationStatus.Adopted; + } + if (o.dynamicSettings && o.dynamicSettings.active === true) { + o.status = ManagedOrganizationStatus.Active; + } + } + } + } + for (const organization of operations.organizations.values()) { + const anOrg = getOrg(organization.name.toLowerCase()); + anOrg.id = organization.id; + } + const orgNames = Array.from(byOrg.keys()).sort(sortByCaseInsensitive); + return res.json({ + apps: apps.map((app) => app.asClientJson()), + orgNames, + orgs: Array.from(byOrg.values()).map((data) => { + return { + name: data.organizationName, + status: data.status, + id: data.id, + configuredInstallations: data.configuredInstallations, + hasDynamicSettings: !!data.dynamicSettings, + appInstallations: Array.from(data.appInstallations.keys()) + .filter((a) => a) + .map((appIdKey) => { + const install = data.appInstallations.get(appIdKey); + return { + installationId: install?.installationId, + appId: install?.app?.id, + }; + }), + }; + }), + }) as unknown as void; + }) +); + +router.use( + '/:appId', + asyncHandler(async function (req: ApiRequestWithGitHubApplication, res: Response, next: NextFunction) { + const { operations } = getProviders(req); + const appId = Number(req.params.appId); + const app = operations.getApplicationById(appId); + if (app) { + req.gitHubApplication = app; + return next(); + } + return next(CreateError.NotFound('no app available with that ID')); + }) +); + +router.use('/:appId', routeIndividualApp); + +router.use('*', (req, res: Response, next: NextFunction) => { + return next(CreateError.NotFound('no API or function available: context/administration/apps')); +}); + +export default router; diff --git a/api/client/context/administration/index.ts b/api/client/context/administration/index.ts index 12889f5aa..cb3e3aab5 100644 --- a/api/client/context/administration/index.ts +++ b/api/client/context/administration/index.ts @@ -3,15 +3,17 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; + import { Organization } from '../../../../business/organization'; import { ReposAppRequest } from '../../../../interfaces'; import { getIsCorporateAdministrator, jsonError } from '../../../../middleware'; import getCompanySpecificDeployment from '../../../../middleware/companySpecificDeployment'; -import { ErrorHelper, getProviders } from '../../../../transitional'; +import { ErrorHelper, getProviders } from '../../../../lib/transitional'; import routeIndividualOrganization from './organization'; +import routeApps from './apps'; const router: Router = Router(); @@ -20,7 +22,7 @@ interface IRequestWithAdministration extends ReposAppRequest { } router.use( - asyncHandler(async (req: IRequestWithAdministration, res, next) => { + asyncHandler(async (req: IRequestWithAdministration, res: Response, next: NextFunction) => { req.isSystemAdministrator = await getIsCorporateAdministrator(req); return next(); }) @@ -28,29 +30,31 @@ router.use( router.get( '/', - asyncHandler(async (req: IRequestWithAdministration, res, next) => { + asyncHandler(async (req: IRequestWithAdministration, res: Response) => { const { operations } = getProviders(req); const isAdministrator = req.isSystemAdministrator; if (!isAdministrator) { return res.json({ isAdministrator, - }); + }) as unknown as void; } const organizations = operations.getOrganizations().map((org) => org.asClientJson()); return res.json({ isAdministrator, organizations, - }); + }) as unknown as void; }) ); -router.use((req: IRequestWithAdministration, res, next) => { +router.use((req: IRequestWithAdministration, res: Response, next: NextFunction) => { return req.isSystemAdministrator ? next() : next(jsonError('Not authorized', 403)); }); +router.use('/apps', routeApps); + router.use( '/organization/:orgName', - asyncHandler(async (req: ReposAppRequest, res, next) => { + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { const { orgName } = req.params; const { operations } = getProviders(req); let organization: Organization = null; @@ -61,7 +65,8 @@ router.use( } catch (noOrgError) { if (ErrorHelper.IsNotFound(noOrgError)) { res.status(404); - return res.end(); + res.end(); + return; } return next(jsonError(noOrgError, 500)); } @@ -74,7 +79,7 @@ const deployment = getCompanySpecificDeployment(); deployment?.routes?.api?.context?.administration?.index && deployment?.routes?.api?.context.administration.index(router); -router.use('*', (req, res, next) => { +router.use('*', (req, res: Response, next: NextFunction) => { return next(jsonError('no API or function available: context/administration', 404)); }); diff --git a/api/client/context/administration/organization/index.ts b/api/client/context/administration/organization/index.ts index 45db6953e..5ed533872 100644 --- a/api/client/context/administration/organization/index.ts +++ b/api/client/context/administration/organization/index.ts @@ -3,8 +3,9 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; + import { ReposAppRequest } from '../../../../../interfaces'; import { jsonError } from '../../../../../middleware'; @@ -16,15 +17,15 @@ router.use('/settings', routeSettings); router.get( '/', - asyncHandler(async (req: ReposAppRequest, res, next) => { + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { const { organization } = req; return res.json({ organization: organization.asClientJson(), - }); + }) as unknown as void; }) ); -router.use('*', (req, res, next) => { +router.use('*', (req, res: Response, next: NextFunction) => { return next(jsonError('no API or function available in administration - organization', 404)); }); diff --git a/api/client/context/administration/organization/settings.ts b/api/client/context/administration/organization/settings.ts index a1c805df2..4c3201fa1 100644 --- a/api/client/context/administration/organization/settings.ts +++ b/api/client/context/administration/organization/settings.ts @@ -3,12 +3,13 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; -import { OrganizationSetting } from '../../../../../entities/organizationSettings/organizationSetting'; + +import { OrganizationSetting } from '../../../../../business/entities/organizationSettings/organizationSetting'; import { ReposAppRequest } from '../../../../../interfaces'; import { jsonError } from '../../../../../middleware'; -import { ErrorHelper, getProviders } from '../../../../../transitional'; +import { CreateError, ErrorHelper, getProviders } from '../../../../../lib/transitional'; const router: Router = Router(); @@ -17,7 +18,7 @@ interface IOrganizationSettings extends ReposAppRequest { } router.use( - asyncHandler(async (req: IOrganizationSettings, res, next) => { + asyncHandler(async (req: IOrganizationSettings, res: Response, next: NextFunction) => { const { organization } = req; const { organizationSettingsProvider } = getProviders(req); try { @@ -34,11 +35,32 @@ router.use( router.get( '/', - asyncHandler(async (req: IOrganizationSettings, res, next) => { + asyncHandler(async (req: IOrganizationSettings, res: Response, next: NextFunction) => { const { dynamicSettings } = req; return res.json({ dynamicSettings, - }); + }) as unknown as void; + }) +); + +router.delete( + '/', + asyncHandler(async function (req: IOrganizationSettings, res: Response) { + const { dynamicSettings } = req; + const { organizationId } = dynamicSettings; + const { organizationSettingsProvider, queryCache } = getProviders(req); + const orgName = req.query.deleteOrganizationConfiguration as string; + if (orgName?.toLowerCase() !== dynamicSettings.organizationName.toLowerCase()) { + throw CreateError.InvalidParameters( + 'The organization name provided does not match the organization name in the configuration.' + ); + } + await organizationSettingsProvider.deleteOrganizationSetting(dynamicSettings); + res.status(204); + if (queryCache) { + queryCache.removeOrganizationById(String(organizationId)); + } + return res.end() as unknown as void; }) ); @@ -46,49 +68,61 @@ router.get( router.get( '/features', - asyncHandler(async (req: IOrganizationSettings, res, next) => { + asyncHandler(async (req: IOrganizationSettings, res: Response, next: NextFunction) => { const { dynamicSettings, organization } = req; const { features } = dynamicSettings; return res.json({ features, organizationName: organization.name, - }); + }) as unknown as void; }) ); router.get( '/feature/:flag', - asyncHandler(async (req: IOrganizationSettings, res, next) => { + asyncHandler(async (req: IOrganizationSettings, res: Response, next: NextFunction) => { const { dynamicSettings, organization } = req; const flag = req.params.flag as string; return res.json({ flag, value: dynamicSettings.features.includes(flag) ? flag : null, organizationName: organization.name, - }); + }) as unknown as void; }) ); router.put( '/feature/:flag', - asyncHandler(async (req: IOrganizationSettings, res, next) => { + asyncHandler(async (req: IOrganizationSettings, res: Response, next: NextFunction) => { const { dynamicSettings, organization } = req; const { insights, organizationSettingsProvider } = getProviders(req); const { features } = dynamicSettings; const flag = req.params.flag as string; + const restart = req.query.restart === '1'; insights?.trackEvent({ name: 'AddOrganizationFeatureFlag', properties: { flag, + restart, currentFeatureFlags: features.join(', '), }, }); - if (features.includes(flag)) { - return next(jsonError(`flag "${flag}" is already set`, 400)); + // special case + if (flag === 'active') { + if (dynamicSettings.active) { + return next(CreateError.InvalidParameters('The organization is already active.')); + } + dynamicSettings.active = true; + } else { + if (features.includes(flag)) { + return next(jsonError(`flag "${flag}" is already set`, 400)); + } + dynamicSettings.features.push(flag); } - dynamicSettings.features.push(flag); try { - dynamicSettings.updated = new Date(); + if (restart) { + dynamicSettings.updated = new Date(); + } await organizationSettingsProvider.updateOrganizationSetting(dynamicSettings); } catch (error) { return next(jsonError(`error adding flag "${flag}": ${error}`, ErrorHelper.GetStatus(error) || 400)); @@ -96,31 +130,43 @@ router.put( return res.json({ flag, value: dynamicSettings.features.includes(flag) ? flag : null, + restart, organizationName: organization.name, - }); + }) as unknown as void; }) ); router.delete( '/feature/:flag', - asyncHandler(async (req: IOrganizationSettings, res, next) => { + asyncHandler(async (req: IOrganizationSettings, res: Response, next: NextFunction) => { const { organization, dynamicSettings } = req; const { organizationSettingsProvider, insights } = getProviders(req); const { features } = dynamicSettings; const flag = req.params.flag as string; + const restart = req.query.restart === '1'; insights?.trackEvent({ name: 'RemoveOrganizationFeatureFlag', properties: { flag, + restart, currentFeatureFlags: features.join(', '), }, }); - if (!features.includes(flag)) { - return next(jsonError(`flag "${flag}" is not set`, 400)); + if (flag === 'active') { + if (!dynamicSettings.active) { + return next(CreateError.InvalidParameters('The organization is already inactive.')); + } + dynamicSettings.active = false; + } else { + if (!features.includes(flag)) { + return next(jsonError(`flag "${flag}" is not set`, 400)); + } + dynamicSettings.features = dynamicSettings.features.filter((flagEntry) => flagEntry !== flag); } - dynamicSettings.features = dynamicSettings.features.filter((flagEntry) => flagEntry !== flag); try { - dynamicSettings.updated = new Date(); + if (restart) { + dynamicSettings.updated = new Date(); + } await organizationSettingsProvider.updateOrganizationSetting(dynamicSettings); } catch (error) { return next(jsonError(`error removing flag "${flag}": ${error}`, ErrorHelper.GetStatus(error) || 400)); @@ -128,8 +174,9 @@ router.delete( return res.json({ flag, value: dynamicSettings.features.includes(flag) ? flag : null, + restart, organizationName: organization.name, - }); + }) as unknown as void; }) ); @@ -137,19 +184,19 @@ router.delete( router.get( '/properties', - asyncHandler(async (req: IOrganizationSettings, res, next) => { + asyncHandler(async (req: IOrganizationSettings, res: Response, next: NextFunction) => { const { dynamicSettings, organization } = req; const { properties } = dynamicSettings; return res.json({ properties, organizationName: organization.name, - }); + }) as unknown as void; }) ); router.get( '/property/:flag', - asyncHandler(async (req: IOrganizationSettings, res, next) => { + asyncHandler(async (req: IOrganizationSettings, res: Response, next: NextFunction) => { const { dynamicSettings, organization } = req; const propertyName = req.params.propertyName as string; const { properties } = dynamicSettings; @@ -157,17 +204,18 @@ router.get( property: propertyName, value: properties[propertyName] || null, organizationName: organization.name, - }); + }) as unknown as void; }) ); router.put( '/property/:propertyName', - asyncHandler(async (req: IOrganizationSettings, res, next) => { + asyncHandler(async (req: IOrganizationSettings, res: Response, next: NextFunction) => { const { organization, dynamicSettings } = req; const { insights, organizationSettingsProvider } = getProviders(req); const { properties } = dynamicSettings; const newValue = req.body.value as string; + const restart = req.query.restart === '1'; if (!newValue) { return next(jsonError('body.value required', 400)); } @@ -180,6 +228,7 @@ router.put( name: 'SetOrganizationSettingProperty', properties: { propertyName, + restart, currentProperties: JSON.stringify(properties), currentPropertyValue, }, @@ -187,7 +236,9 @@ router.put( const updateDescription = `Changing property ${propertyName} value from "${currentPropertyValue}" to "${newValue}"`; dynamicSettings.properties[propertyName] = newValue; try { - dynamicSettings.updated = new Date(); + if (restart) { + dynamicSettings.updated = new Date(); + } await organizationSettingsProvider.updateOrganizationSetting(dynamicSettings); } catch (error) { return next( @@ -202,25 +253,28 @@ router.put( value: properties[propertyName] || null, organizationName: organization.name, dynamicSettings, + restart, updateDescription, - }); + }) as unknown as void; }) ); router.delete( '/property/:propertyName', - asyncHandler(async (req: IOrganizationSettings, res, next) => { + asyncHandler(async (req: IOrganizationSettings, res: Response, next: NextFunction) => { const { organization, dynamicSettings } = req; const { organizationSettingsProvider, insights } = getProviders(req); const { properties } = dynamicSettings; const propertyName = req.params.propertyName as string; const currentPropertyValue = properties[propertyName] || null; + const restart = req.query.restart === '1'; insights?.trackEvent({ name: 'RemoveOrganizationSettingProperty', properties: { propertyName, currentProperties: JSON.stringify(properties), currentPropertyValue, + restart, }, }); if (properties[propertyName] === undefined) { @@ -228,7 +282,9 @@ router.delete( } delete dynamicSettings.properties[propertyName]; try { - dynamicSettings.updated = new Date(); + if (restart) { + dynamicSettings.updated = new Date(); + } await organizationSettingsProvider.updateOrganizationSetting(dynamicSettings); } catch (error) { return next( @@ -239,13 +295,14 @@ router.delete( property: propertyName, value: properties[propertyName] || null, organizationName: organization.name, - }); + restart, + }) as unknown as void; }) ); // -router.use('*', (req, res, next) => { +router.use('*', (req, res: Response, next: NextFunction) => { return next(jsonError('no API or function available in administration - organization', 404)); }); diff --git a/api/client/context/administration/types.ts b/api/client/context/administration/types.ts new file mode 100644 index 000000000..710a2a63b --- /dev/null +++ b/api/client/context/administration/types.ts @@ -0,0 +1,44 @@ +// +// Copyright (c) Microsoft. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +import GitHubApplication, { type GitHubAppDefinition } from '../../../../business/application'; +import { OrganizationSetting } from '../../../../business/entities/organizationSettings/organizationSetting'; +import type { IGitHubAppInstallation, ReposAppRequest } from '../../../../interfaces'; + +export type ApiRequestWithGitHubApplication = ReposAppRequest & { + gitHubApplication: GitHubApplication; +}; + +export enum ManagedOrganizationStatus { + Active = 'Active', + Adopted = 'Adopted', + NotAdopted = 'NotAdopted', +} + +export type ManagedOrganizationAppConfigurationsByOrgView = { + organizationName: string; + status: ManagedOrganizationStatus; + appInstallations: Map; + dynamicSettings: OrganizationSetting; + configuredInstallations: number[]; + id?: number; +}; + +export interface IByOrgViewAppInstallation { + app: GitHubApplication; + installationId?: number; +} + +export type RequestWithInstallation = ApiRequestWithGitHubApplication & { + installation: IGitHubAppInstallation; + organizationDynamicSettings: OrganizationSetting; + organizationStaticSettings: OrganizationSetting; +}; + +export type AdministrativeGitHubAppInstallationResponse = { + app: GitHubAppDefinition; + installationId: number; + dynamicSettings: OrganizationSetting; +}; diff --git a/api/client/context/approvals.ts b/api/client/context/approvals.ts index 3e21e398b..4d7a5b817 100644 --- a/api/client/context/approvals.ts +++ b/api/client/context/approvals.ts @@ -3,11 +3,11 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; import { Team, Organization } from '../../../business'; -import { TeamJoinApprovalEntity } from '../../../entities/teamJoinApproval/teamJoinApproval'; +import { TeamJoinApprovalEntity } from '../../../business/entities/teamJoinApproval/teamJoinApproval'; import { TeamJsonFormat, ReposAppRequest } from '../../../interfaces'; import { jsonError } from '../../../middleware'; import { @@ -16,7 +16,7 @@ import { Approvals_getUserRequests, closeOldRequest, } from '../../../routes/settings/approvals'; -import { getProviders } from '../../../transitional'; +import { getProviders } from '../../../lib/transitional'; import { IndividualContext } from '../../../business/user'; const router: Router = Router(); @@ -30,7 +30,7 @@ const approvalPairToJson = (pair: ApprovalPair) => { router.get( '/', - asyncHandler(async (req: ReposAppRequest, res, next) => { + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { const { approvalProvider, operations } = getProviders(req); const activeContext = (req.individualContext || req.apiContext) as IndividualContext; if (!activeContext.link) { @@ -38,7 +38,7 @@ router.get( teamResponsibilities: [], usersRequests: [], isLinked: false, - }); + }) as unknown as void; } try { // const username = activeContext.getGitHubIdentity().username; @@ -54,7 +54,7 @@ router.get( teamResponsibilities: teamResponsibilities.map(approvalPairToJson), usersRequests: usersRequests.map(approvalPairToJson), }; - return res.json(state); + return res.json(state) as unknown as void; } catch (error) { return next(jsonError(error)); } @@ -65,7 +65,7 @@ router.get( router.get( '/:approvalId', - asyncHandler(async (req: ReposAppRequest, res, next) => { + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { const approvalId = req.params.approvalId; const activeContext = (req.individualContext || req.apiContext) as IndividualContext; if (!activeContext.link) { @@ -112,7 +112,7 @@ router.get( }) ); -router.use('*', (req: ReposAppRequest, res, next) => { +router.use('*', (req: ReposAppRequest, res: Response, next: NextFunction) => { return next(jsonError('Contextual API or route not found within approvals', 404)); }); diff --git a/api/client/context/index.ts b/api/client/context/index.ts index efc9530a7..775716054 100644 --- a/api/client/context/index.ts +++ b/api/client/context/index.ts @@ -3,7 +3,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; import { Organization } from '../../../business'; @@ -11,7 +11,7 @@ import { IProviders, ReposAppRequest } from '../../../interfaces'; import { jsonError } from '../../../middleware'; import getCompanySpecificDeployment from '../../../middleware/companySpecificDeployment'; -import { ErrorHelper, getProviders } from '../../../transitional'; +import { ErrorHelper, getProviders } from '../../../lib/transitional'; import { IndividualContext } from '../../../business/user'; import routeApprovals from './approvals'; @@ -21,6 +21,7 @@ import routeRepos from './repos'; import routeTeams from './teams'; import routeAdministration from './administration'; import routeSample from './sample'; +import routeSettings from './settings'; const router: Router = Router(); @@ -48,7 +49,7 @@ router.get('/', (req: ReposAppRequest, res) => { router.get( '/specialized/multipleLinkGitHubIdentities', - asyncHandler(async (req: ReposAppRequest, res, next) => { + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { const { operations } = getProviders(req); const activeContext = (req.individualContext || req.apiContext) as IndividualContext; const links = (activeContext?.link ? [activeContext.link, ...activeContext.additionalLinks] : []).map( @@ -73,13 +74,13 @@ router.get( response.deletedOrChangedUsernames.push(username); } } - return res.json(response); + return res.json(response) as unknown as void; }) ); router.get( '/accountDetails', - asyncHandler(async (req: ReposAppRequest, res, next) => { + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { const { operations } = getProviders(req); const activeContext = (req.individualContext || req.apiContext) as IndividualContext; try { @@ -104,10 +105,11 @@ router.get('/orgs', routeOrgs); router.get('/repos', routeRepos); router.get('/teams', routeTeams); router.use('/sample', routeSample); +router.use('/settings', routeSettings); router.use( '/orgs/:orgName', - asyncHandler(async (req: ReposAppRequest, res, next) => { + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { const { orgName } = req.params; const providers = getProviders(req); const { operations } = providers; @@ -123,12 +125,15 @@ router.use( return next(); } catch (noOrgError) { if (ErrorHelper.IsNotFound(noOrgError)) { + // Could be either the org truly does not exist, OR, it's uncontrolled. if (await isUnmanagedOrganization(providers, orgName)) { res.status(204); - return res.end(); + res.end(); + return; } res.status(404); - return res.end(); + res.end(); + return; } return next(jsonError(noOrgError, 500)); } @@ -151,7 +156,7 @@ async function isUnmanagedOrganization(providers: IProviders, orgName: string): router.use('/orgs/:orgName', routeIndividualContextualOrganization); -router.use('*', (req: ReposAppRequest, res, next) => { +router.use('*', (req: ReposAppRequest, res: Response, next: NextFunction) => { return next(jsonError('Contextual API or route not found', 404)); }); diff --git a/api/client/context/organization/index.ts b/api/client/context/organization/index.ts index 6bb56a540..323e6f483 100644 --- a/api/client/context/organization/index.ts +++ b/api/client/context/organization/index.ts @@ -3,8 +3,9 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; + import { Organization, Team } from '../../../../business'; import { ReposAppRequest, @@ -16,55 +17,56 @@ import { jsonError } from '../../../../middleware'; import getCompanySpecificDeployment from '../../../../middleware/companySpecificDeployment'; import { IndividualContext } from '../../../../business/user'; -import RouteRepos from './repos'; -import RouteTeams from './teams'; +import routeRepos from './repos'; +import routeTeams from './teams'; +import { CreateError } from '../../../../lib/transitional'; const router: Router = Router(); router.get( '/', - asyncHandler(async (req: ReposAppRequest, res, next) => { + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { const { organization } = req; const activeContext = (req.individualContext || req.apiContext) as IndividualContext; if (!activeContext.link) { - return res.json(false); + return res.json(false) as unknown as void; } const membership = await organization.getOperationalMembership( activeContext.getGitHubIdentity().username ); if (!membership) { - return res.json(false); + return res.json(false) as unknown as void; } return res.json({ user: toSanitizedUser(membership.user), organization: toSanitizedOrg(membership.organization), role: membership.role, state: membership.state, - }); + }) as unknown as void; }) ); router.get( '/sudo', - asyncHandler(async (req: ReposAppRequest, res, next) => { + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { const { organization } = req; const activeContext = (req.individualContext || req.apiContext) as IndividualContext; if (!activeContext.link) { - return res.json({ isSudoer: false }); + return res.json({ isSudoer: false }) as unknown as void; } return res.json({ isSudoer: await organization.isSudoer(activeContext.getGitHubIdentity().username, activeContext.link), - }); + }) as unknown as void; }) ); router.get( '/isOwner', - asyncHandler(async (req: ReposAppRequest, res, next) => { + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { const { organization } = req; const activeContext = (req.individualContext || req.apiContext) as IndividualContext; if (!activeContext.link) { - return res.json({ isOrganizationOwner: false }); + return res.json({ isOrganizationOwner: false }) as unknown as void; } try { const username = activeContext.getGitHubIdentity().username; @@ -72,21 +74,21 @@ router.get( const isOrganizationOwner = membership?.role === OrganizationMembershipRole.Admin; return res.json({ isOrganizationOwner, - }); + }) as unknown as void; } catch (error) { - return jsonError(error, 400); + return next(CreateError.InvalidParameters(error)); } }) ); router.delete( '/', - asyncHandler(async (req: ReposAppRequest, res, next) => { + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { // "Leave" / remove my context const { organization } = req; const activeContext = (req.individualContext || req.apiContext) as IndividualContext; if (!activeContext.link) { - return next(jsonError('You are not linked', 400)); + return next(CreateError.InvalidParameters('You are not linked')); } const login = activeContext.getGitHubIdentity().username; const id = activeContext.getGitHubIdentity().id; @@ -94,26 +96,25 @@ router.delete( await organization.removeMember(login, id); return res.json({ message: `Your ${login} account has been removed from ${organization.name}.`, - }); + }) as unknown as void; } catch (error) { console.warn(error); - return next(jsonError(error, 400)); + return next(CreateError.InvalidParameters(error)); } }) ); router.get( '/personalizedTeams', - asyncHandler(async (req: ReposAppRequest, res, next) => { + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { try { const organization = req.organization as Organization; const activeContext = (req.individualContext || req.apiContext) as IndividualContext; if (!activeContext.link) { - return res.json({ personalizedTeams: [] }); + return res.json({ personalizedTeams: [] }) as unknown as void; } const userAggregateContext = activeContext.aggregations; const maintainedTeams = new Set(); - const broadTeams = new Set(req.organization.broadAccessTeams); const userTeams = userAggregateContext.reduceOrganizationTeams( organization, await userAggregateContext.teams() @@ -131,21 +132,21 @@ router.get( }); return res.json({ personalizedTeams, - }); + }) as unknown as void; } catch (error) { - return next(jsonError(error, 400)); + return next(CreateError.InvalidParameters(error)); } }) ); -router.use('/repos', RouteRepos); -router.use('/teams', RouteTeams); +router.use('/repos', routeRepos); +router.use('/teams', routeTeams); const deployment = getCompanySpecificDeployment(); deployment?.routes?.api?.context?.organization?.index && deployment?.routes?.api?.context?.organization?.index(router); -router.use('*', (req, res, next) => { +router.use('*', (req: ReposAppRequest, res: Response, next: NextFunction) => { return next(jsonError('no API or function available: client>organization', 404)); }); @@ -159,6 +160,7 @@ const toSanitizedUser = (user) => { avatar_url: user.avatar_url, }; }; + const toSanitizedOrg = (org) => { if (!org || !org.login) { return undefined; diff --git a/api/client/context/organization/repo.ts b/api/client/context/organization/repo.ts index e995886f8..14c337022 100644 --- a/api/client/context/organization/repo.ts +++ b/api/client/context/organization/repo.ts @@ -3,7 +3,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; import { @@ -21,9 +21,9 @@ const router: Router = Router(); router.get( '/permissions', AddRepositoryPermissionsToRequest, - asyncHandler(async (req: ReposAppRequest, res, next) => { + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { const permissions = getContextualRepositoryPermissions(req); - return res.json(permissions); + return res.json(permissions) as unknown as void; }) ); @@ -33,7 +33,7 @@ const deployment = getCompanySpecificDeployment(); deployment?.routes?.api?.context?.organization?.repo && deployment?.routes?.api?.context?.organization?.repo(router); -router.use('*', (req, res, next) => { +router.use('*', (req, res: Response, next: NextFunction) => { return next(jsonError(`no API or ${req.method} function available for repo`, 404)); }); diff --git a/api/client/context/organization/repoForkUnlock.ts b/api/client/context/organization/repoForkUnlock.ts index 55fa0935e..1b7aadd81 100644 --- a/api/client/context/organization/repoForkUnlock.ts +++ b/api/client/context/organization/repoForkUnlock.ts @@ -3,24 +3,21 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; import { jsonError } from '../../../../middleware'; import { getRepositoryMetadataProvider, ReposAppRequest } from '../../../../interfaces'; import { Organization } from '../../../../business'; -import { - getContextualRepository, - getContextualRepositoryPermissions, -} from '../../../../middleware/github/repoPermissions'; +import { getContextualRepository } from '../../../../middleware/github/repoPermissions'; import { IndividualContext } from '../../../../business/user'; -import { ErrorHelper, getProviders } from '../../../../transitional'; -import NewRepositoryLockdownSystem from '../../../../features/newRepositories/newRepositoryLockdown'; +import { ErrorHelper, getProviders } from '../../../../lib/transitional'; +import NewRepositoryLockdownSystem from '../../../../business/features/newRepositories/newRepositoryLockdown'; const router: Router = Router(); router.use( - asyncHandler(async (req: ReposAppRequest, res, next) => { + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { const organization = req.organization as Organization; if (!organization.isNewRepositoryLockdownSystemEnabled()) { return next(jsonError('This endpoint is not available as configured for the organization', 400)); @@ -42,12 +39,13 @@ router.use( router.post( '/approve', - asyncHandler(async (req: ReposAppRequest, res, next) => { - const { operations } = getProviders(req); + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { + const { insights, operations } = getProviders(req); const repository = getContextualRepository(req); const repositoryMetadataProvider = getRepositoryMetadataProvider(operations); const organization = repository.organization; const lockdownSystem = new NewRepositoryLockdownSystem({ + insights, operations, organization, repository, @@ -58,7 +56,7 @@ router.post( return res.json({ message: `Unlocked the ${repository.name} repo in the ${organization.name} org`, unlocked: true, - }); + }) as unknown as void; } catch (error) { return next( jsonError( @@ -70,7 +68,7 @@ router.post( }) ); -router.use('*', (req, res, next) => { +router.use('*', (req, res: Response, next: NextFunction) => { return next(jsonError(`no API or ${req.method} function available for repo fork unlock`, 404)); }); diff --git a/api/client/context/organization/repos.ts b/api/client/context/organization/repos.ts index 851aa48c2..c124647a3 100644 --- a/api/client/context/organization/repos.ts +++ b/api/client/context/organization/repos.ts @@ -3,21 +3,22 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; + import { Repository } from '../../../../business'; import { jsonError } from '../../../../middleware'; import { setContextualRepository } from '../../../../middleware/github/repoPermissions'; -import { OrganizationMembershipState, ReposAppRequest } from '../../../../interfaces'; +import { OrganizationMembershipState, ReposAppRequest, VoidedExpressRoute } from '../../../../interfaces'; import { IndividualContext } from '../../../../business/user'; import { createRepositoryFromClient } from '../../newOrgRepo'; -import RouteContextualRepo from './repo'; +import routeContextualRepo from './repo'; const router: Router = Router(); -async function validateActiveMembership(req: ReposAppRequest, res, next) { +async function validateActiveMembership(req: ReposAppRequest, res: Response, next: NextFunction) { const { organization } = req; const activeContext = (req.individualContext || req.apiContext) as IndividualContext; if (!activeContext.link) { @@ -33,11 +34,15 @@ async function validateActiveMembership(req: ReposAppRequest, res, next) { return next(); } -router.post('/', asyncHandler(validateActiveMembership), asyncHandler(createRepositoryFromClient)); +router.post( + '/', + asyncHandler(validateActiveMembership), + asyncHandler(createRepositoryFromClient as VoidedExpressRoute) +); router.use( '/:repoName', - asyncHandler(async (req: ReposAppRequest, res, next) => { + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { const { organization } = req; const { repoName } = req.params; let repository: Repository = null; @@ -47,9 +52,9 @@ router.use( }) ); -router.use('/:repoName', RouteContextualRepo); +router.use('/:repoName', routeContextualRepo); -router.use('*', (req, res, next) => { +router.use('*', (req, res: Response, next: NextFunction) => { return next(jsonError('no API or function available for repos', 404)); }); diff --git a/api/client/context/organization/team.ts b/api/client/context/organization/team.ts index 8574d37fb..649de7635 100644 --- a/api/client/context/organization/team.ts +++ b/api/client/context/organization/team.ts @@ -3,10 +3,10 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; -import { TeamJoinApprovalEntity } from '../../../../entities/teamJoinApproval/teamJoinApproval'; +import { TeamJoinApprovalEntity } from '../../../../business/entities/teamJoinApproval/teamJoinApproval'; import { ReposAppRequest, OrganizationMembershipState, @@ -24,8 +24,9 @@ import { import { submitTeamJoinRequest } from '../../../../routes/org/team'; import { postActionDecision, TeamApprovalDecision } from '../../../../routes/org/team/approval'; import { PermissionWorkflowEngine } from '../../../../routes/org/team/approvals'; -import { getProviders } from '../../../../transitional'; +import { CreateError, getProviders } from '../../../../lib/transitional'; import { IndividualContext } from '../../../../business/user'; +import getCompanySpecificDeployment from '../../../../middleware/companySpecificDeployment'; const router: Router = Router(); @@ -42,16 +43,16 @@ router.get( '/permissions', asyncHandler(AddTeamPermissionsToRequest), asyncHandler(AddTeamMembershipToRequest), - asyncHandler(async (req: ReposAppRequest, res, next) => { + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { const membership = getTeamMembershipFromRequest(req); const permissions = getTeamPermissionsFromRequest(req); - return res.json({ permissions, membership }); + return res.json({ permissions, membership }) as unknown as void; }) ); router.get( '/join/request', - asyncHandler(async (req: ReposAppRequest, res, next) => { + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { const { approvalProvider } = getProviders(req); const team = getContextualTeam(req); const activeContext = (req.individualContext || req.apiContext) as IndividualContext; @@ -63,32 +64,49 @@ router.get( request = approvals.length > 0 ? approvals[0] : null; } const response: ITeamRequestJsonResponse = { request }; - return res.json(response); + return res.json(response) as unknown as void; }) ); router.post( '/join', asyncHandler(AddTeamMembershipToRequest), - asyncHandler(async (req: ReposAppRequest, res, next) => { + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { try { const providers = getProviders(req); const { approvalProvider } = providers; const membership = getTeamMembershipFromRequest(req); if (!membership.isLinked) { - return res.json({ error: 'You have not linked your GitHub account to your corporate identity yet' }); + return res.json({ + error: 'You have not linked your GitHub account to your corporate identity yet', + }) as unknown as void; } if (membership.membershipState === OrganizationMembershipState.Active) { - return res.json({ error: 'You already have an active team membership' }); + return res.json({ error: 'You already have an active team membership' }) as unknown as void; } const team = getContextualTeam(req); const activeContext = (req.individualContext || req.apiContext) as IndividualContext; + const companySpecific = getCompanySpecificDeployment(); + if (companySpecific?.middleware?.teamPermissions.beforeJoinRequest) { + try { + const optionalOutcome = await companySpecific.middleware.teamPermissions.beforeJoinRequest( + providers, + activeContext, + team + ); + if (optionalOutcome) { + return res.json(optionalOutcome) as unknown as void; + } + } catch (error) { + return next(error); + } + } // no point query currently implemented let approvals = await approvalProvider.queryPendingApprovalsForTeam(String(team.id)); approvals = approvals.filter((approval) => approval.corporateId === activeContext.corporateIdentity.id); const request = approvals.length > 0 ? approvals[0] : null; if (request) { - return res.json({ error: 'You already have a pending team join request' }); + return res.json({ error: 'You already have a pending team join request' }) as unknown as void; } // const justification = (req.body.justification || '') as string; @@ -102,7 +120,7 @@ router.post( correlationId, hostname ); - return res.json(outcome); + return res.json(outcome) as unknown as void; } catch (error) { return next(jsonError(error)); } @@ -112,21 +130,21 @@ router.post( router.post( '/join/approvals/:approvalId', asyncHandler(AddTeamPermissionsToRequest), - asyncHandler(async (req: ReposAppRequest, res, next) => { + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { const { approvalId: id } = req.params; if (!id) { - return next(jsonError('invalid approval', 400)); + return next(CreateError.InvalidParameters('invalid approval')); } const permissions = getTeamPermissionsFromRequest(req); if (!permissions.allowAdministration) { - return next(jsonError('you do not have permission to administer this team', 401)); + return next(CreateError.NotAuthorized('you do not have permission to administer this team')); } const providers = getProviders(req); const { approvalProvider, operations } = providers; const team = getContextualTeam(req); const request = await approvalProvider.getApprovalEntity(id); if (String(request.teamId) !== String(team.id)) { - return next(jsonError('mismatch on team', 400)); + return next(CreateError.InvalidParameters('mismatch on team')); } const requestingUser = await operations.getAccountWithDetailsAndLink(request.thirdPartyId); const approvalPackage = { request, requestingUser, id }; @@ -146,7 +164,7 @@ router.post( decision = TeamApprovalDecision.Reopen; break; default: - return next(jsonError('invalid or no decision type', 400)); + return next(CreateError.InvalidParameters('invalid or no decision type')); } const teamBaseUrl = `/orgs/${team.organization.name}/teams/${team.slug}/`; // trailing? try { @@ -154,9 +172,9 @@ router.post( if (outcome.error) { throw outcome.error; } - return res.json(outcome); + return res.json(outcome) as unknown as void; } catch (outcomeError) { - return next(jsonError(outcomeError, 500)); + return next(CreateError.ServerError(outcomeError)); } }) ); @@ -164,21 +182,21 @@ router.post( router.get( '/join/approvals/:approvalId', asyncHandler(AddTeamPermissionsToRequest), - asyncHandler(async (req: ReposAppRequest, res, next) => { + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { const { approvalId: id } = req.params; if (!id) { - return next(jsonError('invalid approval', 400)); + return next(CreateError.InvalidParameters('invalid approval')); } const permissions = getTeamPermissionsFromRequest(req); if (!permissions.allowAdministration) { - return next(jsonError('you do not have permission to administer this team', 401)); + return next(CreateError.NotAuthorized('you do not have permission to administer this team')); } const providers = getProviders(req); const { approvalProvider, graphProvider } = providers; const team = getContextualTeam(req); const request = await approvalProvider.getApprovalEntity(id); if (String(request.teamId) !== String(team.id)) { - return next(jsonError('mismatch on team', 400)); + return next(CreateError.InvalidParameters('mismatch on team')); } let management: IGraphEntry[] = null; if (request?.corporateId) { @@ -188,14 +206,14 @@ router.get( // we ignore any failure here, this is an optional value-add for now } } - return res.json({ approval: request, management }); + return res.json({ approval: request, management }) as unknown as void; }) ); router.get( '/join/approvals', asyncHandler(AddTeamPermissionsToRequest), - asyncHandler(async (req: ReposAppRequest, res, next) => { + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { const { approvalProvider } = getProviders(req); const team = getContextualTeam(req); const permissions = getTeamPermissionsFromRequest(req); @@ -206,22 +224,22 @@ router.get( response.allowAdministration = permissions.allowAdministration; response.approvals = await approvalProvider.queryPendingApprovalsForTeam(String(team.id)); } - return res.json(response); + return res.json(response) as unknown as void; }) ); router.post( '/role/:login', asyncHandler(AddTeamPermissionsToRequest), - asyncHandler(async (req: ReposAppRequest, res, next) => { + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { const { role } = req.body; const { login } = req.params; if (!login) { - return next(jsonError('invalid login', 400)); + return next(CreateError.InvalidParameters('invalid login')); } const permissions = getTeamPermissionsFromRequest(req); if (!permissions.allowAdministration) { - return next(jsonError('you do not have permission to administer this team', 401)); + return next(CreateError.NotAuthorized('you do not have permission to administer this team')); } const team = getContextualTeam(req); try { @@ -230,17 +248,21 @@ router.post( !currentRole || (currentRole as ITeamMembershipRoleState).state !== OrganizationMembershipState.Active ) { - return next(jsonError(`${login} is not currently a member of the team`, 400)); + return next(CreateError.InvalidParameters(`${login} is not currently a member of the team`)); } const response = await team.addMembership(login, { role }); - return res.json(response); + return res.json(response) as unknown as void; } catch (outcomeError) { - return next(jsonError(outcomeError, 500)); + return next(CreateError.ServerError(outcomeError)); } }) ); -router.use('*', (req, res, next) => { +const deployment = getCompanySpecificDeployment(); +deployment?.routes?.api?.context?.organization?.team && + deployment?.routes?.api?.context?.organization?.team(router); + +router.use('*', (req, res: Response, next: NextFunction) => { return next(jsonError('no API or function available for contextual team', 404)); }); diff --git a/api/client/context/organization/teams.ts b/api/client/context/organization/teams.ts index 99781f31b..3cb9ffeea 100644 --- a/api/client/context/organization/teams.ts +++ b/api/client/context/organization/teams.ts @@ -3,8 +3,9 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; + import { Team } from '../../../../business'; import { jsonError } from '../../../../middleware'; import { setContextualTeam } from '../../../../middleware/github/teamPermissions'; @@ -18,7 +19,7 @@ const router: Router = Router(); router.use( '/:teamSlug', - asyncHandler(async (req: ReposAppRequest, res, next) => { + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { const { organization } = req; const { teamSlug } = req.params; let team: Team = null; @@ -35,7 +36,7 @@ router.use( router.use('/:teamSlug', RouteTeam); -router.use('*', (req, res, next) => { +router.use('*', (req, res: Response, next: NextFunction) => { return next(jsonError('no API or function available for repos', 404)); }); diff --git a/api/client/context/orgs.ts b/api/client/context/orgs.ts index fd242331f..962e43f44 100644 --- a/api/client/context/orgs.ts +++ b/api/client/context/orgs.ts @@ -15,7 +15,7 @@ export default asyncHandler(async (req: ReposAppRequest, res) => { member: [], admin: [], isLinked: false, - }); + }) as unknown as void; } const orgs = await activeContext.aggregations.getQueryCacheOrganizations(); const data = { @@ -33,5 +33,5 @@ export default asyncHandler(async (req: ReposAppRequest, res) => { }; }), }; - return res.json(data); + return res.json(data) as unknown as void; }); diff --git a/api/client/context/repos.ts b/api/client/context/repos.ts index e446dbda3..2af5ab598 100644 --- a/api/client/context/repos.ts +++ b/api/client/context/repos.ts @@ -3,19 +3,20 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +import { NextFunction, Response } from 'express'; import asyncHandler from 'express-async-handler'; import { GitHubRepositoryPermission, ReposAppRequest } from '../../../interfaces'; import { IndividualContext } from '../../../business/user'; -export default asyncHandler(async (req: ReposAppRequest, res, next) => { +export default asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { try { const activeContext = (req.individualContext || req.apiContext) as IndividualContext; if (!activeContext.link) { return res.json({ isLinked: false, repositories: [], - }); + }) as unknown as void; } let permissions = await activeContext.aggregations.getQueryCacheRepositoryPermissions(); permissions = permissions.filter((perm) => { @@ -47,7 +48,7 @@ export default asyncHandler(async (req: ReposAppRequest, res, next) => { // TODO: would be nice for team permission for repos to also store the team slug in the query cache! }; }), - }); + }) as unknown as void; } catch (error) { return next(error); } diff --git a/api/client/context/sample.ts b/api/client/context/sample.ts index e957ab121..687377ea7 100644 --- a/api/client/context/sample.ts +++ b/api/client/context/sample.ts @@ -3,20 +3,20 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; import { sendLinkedAccountMail } from '../../../business/operations/link'; import { ReposAppRequest } from '../../../interfaces'; import { jsonError } from '../../../middleware'; -import { CreateError, getProviders } from '../../../transitional'; +import { CreateError, getProviders } from '../../../lib/transitional'; import { IndividualContext } from '../../../business/user'; const router: Router = Router(); router.get( '/:templateName', - asyncHandler(async (req: ReposAppRequest, res, next) => { + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { const { operations } = getProviders(req); const activeContext = (req.individualContext || req.apiContext) as IndividualContext; const templateName = req.params.templateName as string; @@ -39,11 +39,11 @@ router.get( } catch (error) { return next(error); } - return res.json({ templateName }); + return res.json({ templateName }) as unknown as void; }) ); -router.use('*', (req: ReposAppRequest, res, next) => { +router.use('*', (req: ReposAppRequest, res: Response, next: NextFunction) => { return next(jsonError('Contextual API or route not found within samples', 404)); }); diff --git a/api/client/context/settings.ts b/api/client/context/settings.ts new file mode 100644 index 000000000..86a72a256 --- /dev/null +++ b/api/client/context/settings.ts @@ -0,0 +1,48 @@ +// +// Copyright (c) Microsoft. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +import { NextFunction, Response, Router } from 'express'; +import asyncHandler from 'express-async-handler'; + +import { ReposAppRequest } from '../../../interfaces'; +import { CreateError, getProviders } from '../../../lib/transitional'; +import { getUserSettings } from '../../../middleware/business/userSettings'; + +import type { ReposAppRequestWithUserSettings } from '../../../interfaces/middleware'; + +const router: Router = Router(); + +router.use(asyncHandler(getUserSettings)); + +router.get( + '/', + asyncHandler(async (req: ReposAppRequestWithUserSettings, res: Response, next: NextFunction) => { + const { userSettings } = req; + return res.json(userSettings || {}) as unknown as any; + }) +); + +async function setPublicDataSharingValue( + sharingOptOn: boolean, + req: ReposAppRequestWithUserSettings, + res: Response, + next: NextFunction +) { + const { userSettings } = req; + const { userSettingsProvider } = getProviders(req); + userSettings.contributionShareOptIn = sharingOptOn; + await userSettingsProvider.updateUserSettings(userSettings); + return res.status(201).json(userSettings || {}) as unknown as any; +} + +// Actions as separate posts to keep the API simple +router.post('/publicDataSharing/optIn', asyncHandler(setPublicDataSharingValue.bind(null, true))); +router.post('/publicDataSharing/optOut', asyncHandler(setPublicDataSharingValue.bind(null, false))); + +router.use('*', (req: ReposAppRequest, res: Response, next: NextFunction) => { + return next(CreateError.NotFound('Contextual API route not found: /settings')); +}); + +export default router; diff --git a/api/client/context/teams.ts b/api/client/context/teams.ts index 4ffc3405e..ad45b7a43 100644 --- a/api/client/context/teams.ts +++ b/api/client/context/teams.ts @@ -4,23 +4,24 @@ // import asyncHandler from 'express-async-handler'; +import { NextFunction, Response } from 'express'; import { ReposAppRequest, TeamJsonFormat } from '../../../interfaces'; import { IndividualContext } from '../../../business/user'; -export default asyncHandler(async (req: ReposAppRequest, res, next) => { +export default asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { const activeContext = (req.individualContext || req.apiContext) as IndividualContext; if (!activeContext.link) { return res.json({ isLinked: false, member: [], maintainer: [], - }); + }) as unknown as void; } const permissions = await activeContext.aggregations.getQueryCacheTeams(); return res.json({ isLinked: true, member: permissions.member.map((t) => t.asJson(TeamJsonFormat.Augmented)), maintainer: permissions.maintainer.map((t) => t.asJson(TeamJsonFormat.Augmented)), - }); + }) as unknown as void; }); diff --git a/api/client/index.ts b/api/client/index.ts index e1b4538b3..db0e86073 100644 --- a/api/client/index.ts +++ b/api/client/index.ts @@ -3,7 +3,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; import { @@ -12,8 +12,9 @@ import { requireAccessTokenClient, setIdentity, jsonError, + requireAuthenticatedUserOrSignIn, } from '../../middleware'; -import { getProviders } from '../../transitional'; +import { getProviders } from '../../lib/transitional'; import getCompanySpecificDeployment from '../../middleware/companySpecificDeployment'; @@ -30,13 +31,19 @@ import routeNews from './news'; import routeCrossOrganizationPeople from './people'; import routeCrossOrganizationRepos from './repos'; import routeCrossOrganizationTeams from './teams'; +import routeUsers from './users'; const router: Router = Router(); -router.use((req: ReposAppRequest, res, next) => { +router.use((req: ReposAppRequest, res: Response, next: NextFunction) => { const { config } = getProviders(req); if (config?.features?.allowApiClient) { - return req.isAuthenticated() ? next() : next(jsonError('Session is not authenticated', 401)); + if (req.isAuthenticated()) { + return next(); + } else if (req.query.authenticate === 'session') { + return requireAuthenticatedUserOrSignIn(req, res, next); + } + return next(jsonError('Session is not authenticated', 401)); } return next(jsonError('Client API features unavailable', 403)); }); @@ -57,6 +64,7 @@ router.use('/signout', routeSession); router.use('/people', routeCrossOrganizationPeople); router.use('/repos', routeCrossOrganizationRepos); router.use('/teams', routeCrossOrganizationTeams); +router.use('/users', routeUsers); router.use('/news', routeNews); const dynamicStartupInstance = getCompanySpecificDeployment(); @@ -81,7 +89,7 @@ router.get('/', (req: ReposAppRequest, res) => { appService: config?.webServer?.appService?.name ? { name: config?.webServer?.appService?.name, - slot: config?.webServer?.appService?.slot, + slot: config?.webServer?.appService?.advanced?.slotType || config?.webServer?.appService?.slot, region: config?.webServer?.appService?.region, } : undefined, @@ -120,7 +128,7 @@ router.get('/', (req: ReposAppRequest, res) => { return res.send(JSON.stringify(data, null, 2)); }); -router.use((req, res, next) => { +router.use((req, res: Response, next: NextFunction) => { return next(jsonError('The resource or endpoint you are looking for is not there', 404)); }); diff --git a/api/client/linking.ts b/api/client/linking.ts index 918af1e7d..c3188b951 100644 --- a/api/client/linking.ts +++ b/api/client/linking.ts @@ -3,19 +3,19 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; import { IndividualContext } from '../../business/user'; import { jsonError } from '../../middleware'; -import { ErrorHelper, getProviders } from '../../transitional'; +import { ErrorHelper, getProviders } from '../../lib/transitional'; import { unlinkInteractive } from '../../routes/unlink'; import { interactiveLinkUser } from '../../routes/link'; import { ReposAppRequest } from '../../interfaces'; const router: Router = Router(); -async function validateLinkOk(req: ReposAppRequest, res, next) { +async function validateLinkOk(req: ReposAppRequest, res: Response, next: NextFunction) { const activeContext = (req.individualContext || req.apiContext) as IndividualContext; const providers = getProviders(req); const insights = providers.insights; @@ -91,7 +91,7 @@ async function validateLinkOk(req: ReposAppRequest, res, next) { } } -router.get('/banner', (req: ReposAppRequest, res, next) => { +router.get('/banner', (req: ReposAppRequest, res: Response, next: NextFunction) => { const { config } = getProviders(req); const offline = config?.github?.links?.provider?.linkingOfflineMessage; return res.json({ offline }); @@ -99,7 +99,7 @@ router.get('/banner', (req: ReposAppRequest, res, next) => { router.delete( '/', - asyncHandler(async (req: ReposAppRequest, res, next) => { + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { const activeContext = (req.individualContext || req.apiContext) as IndividualContext; return unlinkInteractive(true, activeContext, req, res, next); }) @@ -108,13 +108,13 @@ router.delete( router.post( '/', validateLinkOk, - asyncHandler(async (req: ReposAppRequest, res, next) => { + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { const activeContext = (req.individualContext || req.apiContext) as IndividualContext; return interactiveLinkUser(true, activeContext, req, res, next); }) ); -router.use('*', (req: ReposAppRequest, res, next) => { +router.use('*', (req: ReposAppRequest, res: Response, next: NextFunction) => { return next(jsonError('API or route not found', 404)); }); diff --git a/api/client/newOrgRepo.ts b/api/client/newOrgRepo.ts index 280dd8879..0984312b3 100644 --- a/api/client/newOrgRepo.ts +++ b/api/client/newOrgRepo.ts @@ -3,19 +3,24 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import _ from 'lodash'; + +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; const router: Router = Router(); -import _ from 'lodash'; - -import { getProviders } from '../../transitional'; +import { getProviders } from '../../lib/transitional'; import { jsonError } from '../../middleware/jsonError'; import { IndividualContext } from '../../business/user'; import { Organization } from '../../business/organization'; import { CreateRepository, ICreateRepositoryApiResult, CreateRepositoryEntrypoint } from '../createRepo'; import { Team } from '../../business/team'; -import { GitHubRepositoryVisibility, GitHubTeamRole, ReposAppRequest } from '../../interfaces'; +import { + GitHubRepositoryVisibility, + GitHubTeamRole, + ReposAppRequest, + VoidedExpressRoute, +} from '../../interfaces'; // This file supports the client apps for creating repos. @@ -25,7 +30,7 @@ interface ILocalApiRequest extends ReposAppRequest { knownRequesterMailAddress?: string; } -router.get('/metadata', (req: ILocalApiRequest, res, next) => { +router.get('/metadata', (req: ILocalApiRequest, res: Response, next: NextFunction) => { try { const options = { projectType: req.query.projectType, @@ -40,12 +45,13 @@ router.get('/metadata', (req: ILocalApiRequest, res, next) => { router.get( '/personalizedTeams', - asyncHandler(async (req: ILocalApiRequest, res, next) => { + asyncHandler(async (req: ILocalApiRequest, res: Response, next: NextFunction) => { try { const organization = req.organization as Organization; const userAggregateContext = req.apiContext.aggregations; const maintainedTeams = new Set(); const broadTeams = new Set(req.organization.broadAccessTeams); + const openAccessTeams = new Set(req.organization.openAccessTeams); const userTeams = userAggregateContext.reduceOrganizationTeams( organization, await userAggregateContext.teams() @@ -57,6 +63,7 @@ router.get( const personalizedTeams = Array.from(combinedTeams.values()).map((combinedTeam) => { return { broad: broadTeams.has(Number(combinedTeam.id)), + isOpenAccessTeam: openAccessTeams.has(Number(combinedTeam.id)), description: combinedTeam.description, id: Number(combinedTeam.id), name: combinedTeam.name, @@ -67,7 +74,7 @@ router.get( }); return res.json({ personalizedTeams, - }); + }) as unknown as void; } catch (error) { return next(jsonError(error, 400)); } @@ -76,11 +83,12 @@ router.get( router.get( '/teams', - asyncHandler(async (req: ILocalApiRequest, res, next) => { + asyncHandler(async (req: ILocalApiRequest, res: Response, next: NextFunction) => { const providers = getProviders(req); const queryCache = providers.queryCache; const organization = req.organization as Organization; const broadTeams = new Set(organization.broadAccessTeams); + const openAccessTeams = new Set(req.organization.openAccessTeams); if (req.query.refresh === undefined && queryCache && queryCache.supportsTeams) { // Use the newer method in this case... const organizationTeams = await queryCache.organizationTeams(organization.id.toString()); @@ -91,9 +99,12 @@ router.get( if (broadTeams.has(Number(t.id))) { t['broad'] = true; } + if (openAccessTeams.has(Number(t.id))) { + t['openAccess'] = true; + } return t; }), - }); + }) as unknown as void; } // By default, allow a 30-second old list of teams. If the cached @@ -154,7 +165,7 @@ router.get( }) ); -export async function discoverUserIdentities(req: ReposAppRequest, res, next) { +export async function discoverUserIdentities(req: ReposAppRequest, res: Response, next: NextFunction) { const apiContext = req.apiContext as IndividualContext; const providers = getProviders(req); const mailAddressProvider = providers.mailAddressProvider; @@ -175,12 +186,16 @@ export async function discoverUserIdentities(req: ReposAppRequest, res, next) { return next(); } -router.post('/repo/:repo', asyncHandler(discoverUserIdentities), asyncHandler(createRepositoryFromClient)); +router.post( + '/repo/:repo', + asyncHandler(discoverUserIdentities), + asyncHandler(createRepositoryFromClient as VoidedExpressRoute) +); -export async function createRepositoryFromClient(req: ILocalApiRequest, res, next) { +export async function createRepositoryFromClient(req: ILocalApiRequest, res: Response, next: NextFunction) { const providers = getProviders(req); const { insights, diagnosticsDrop, customizedNewRepositoryLogic, graphProvider } = providers; - const individualContext = req.individualContext || req.apiContext; + const individualContext = req.watchdogContextOverride || req.individualContext || req.apiContext; const config = getProviders(req).config; const organization = (req.organization || (req as any).aeOrganization) as Organization; const existingRepoId = req.body.existingrepoid; diff --git a/api/client/newRepo.ts b/api/client/newRepo.ts index 26aca31e6..8d28cae1c 100644 --- a/api/client/newRepo.ts +++ b/api/client/newRepo.ts @@ -3,16 +3,16 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; const router: Router = Router(); -import { getProviders } from '../../transitional'; +import { getProviders } from '../../lib/transitional'; import { jsonError } from '../../middleware/jsonError'; import newOrgRepo from './newOrgRepo'; import { ReposAppRequest } from '../../interfaces'; -router.use('/org/:org', (req: ReposAppRequest, res, next) => { +router.use('/org/:org', (req: ReposAppRequest, res: Response, next: NextFunction) => { const orgName = req.params.org; const { operations } = getProviders(req); try { diff --git a/api/client/news.ts b/api/client/news.ts index b33739fa7..24cb920a0 100644 --- a/api/client/news.ts +++ b/api/client/news.ts @@ -3,11 +3,11 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; import { jsonError } from '../../middleware'; -import { getProviders } from '../../transitional'; +import { getProviders } from '../../lib/transitional'; import { ReposAppRequest } from '../../interfaces'; const router: Router = Router(); @@ -16,11 +16,11 @@ router.get( '/', asyncHandler(async (req: ReposAppRequest, res) => { const { config } = getProviders(req); - return res.json({ articles: config?.news?.all || [] }); + return res.json({ articles: config?.news?.all || [] }) as unknown as void; }) ); -router.use('*', (req: ReposAppRequest, res, next) => { +router.use('*', (req: ReposAppRequest, res: Response, next: NextFunction) => { return next(jsonError('API or route not found within news', 404)); }); diff --git a/api/client/organization/annotations.ts b/api/client/organization/annotations.ts index 834e2c130..764ac8b63 100644 --- a/api/client/organization/annotations.ts +++ b/api/client/organization/annotations.ts @@ -3,7 +3,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; import { jsonError } from '../../../middleware/jsonError'; @@ -19,10 +19,12 @@ import { import { IOrganizationAnnotationChange, OrganizationAnnotation, -} from '../../../entities/organizationAnnotation'; -import { ErrorHelper, getProviders } from '../../../transitional'; + getOrganizationAnnotationRestrictedPropertyNames, +} from '../../../business/entities/organizationAnnotation'; +import { CreateError, ErrorHelper, getProviders } from '../../../lib/transitional'; import { IndividualContext } from '../../../business/user'; import { IProviders } from '../../../interfaces'; +import { ensureOrganizationProfileMiddleware } from '../../../middleware/github/ensureOrganizationProfile'; const router: Router = Router(); @@ -33,7 +35,7 @@ type IRequestWithOrganizationAnnotations = IReposAppRequestWithOrganizationManag router.use( '/', checkIsCorporateAdministrator, - asyncHandler(async (req: IRequestWithOrganizationAnnotations, res, next) => { + asyncHandler(async (req: IRequestWithOrganizationAnnotations, res: Response, next: NextFunction) => { const { organizationAnnotationsProvider } = getProviders(req); const { organization, organizationManagementType, organizationProfile } = req; const organizationId = @@ -53,24 +55,28 @@ router.use( router.get( '/', - asyncHandler(async (req: IRequestWithOrganizationAnnotations, res, next) => { + asyncHandler(async (req: IRequestWithOrganizationAnnotations, res: Response, next: NextFunction) => { const { annotations } = req; // Limited redaction + const annotation = { ...annotations }; const isSystemAdministrator = await getIsCorporateAdministrator(req); - if (!isSystemAdministrator && annotations.administratorNotes) { - annotations.administratorNotes = '*****'; - } - if (!isSystemAdministrator && annotations?.history?.length > 0) { - delete annotations.history; + for (const propertyToRedact of getOrganizationAnnotationRestrictedPropertyNames(isSystemAdministrator)) { + delete annotation[propertyToRedact]; } return res.json({ isSystemAdministrator, - annotations, - }); + annotations: annotation, + }) as unknown as void; }) ); -async function ensureAnnotations(req: IRequestWithOrganizationAnnotations, res, next) { +router.use(ensureOrganizationProfileMiddleware); + +async function ensureAnnotations( + req: IRequestWithOrganizationAnnotations, + res: Response, + next: NextFunction +) { if (!req.annotations) { const { organizationAnnotationsProvider } = getProviders(req); try { @@ -80,7 +86,7 @@ async function ensureAnnotations(req: IRequestWithOrganizationAnnotations, res, await organizationAnnotationsProvider.insertAnnotations(annotations); req.annotations = annotations; } catch (error) { - return next(error); + return next(jsonError(error)); } } return next(); @@ -91,11 +97,11 @@ router.put('*', AuthorizeOnlyCorporateAdministrators, ensureAnnotations); router.put( '/', - asyncHandler(async (req: IRequestWithOrganizationAnnotations, res, next) => { + asyncHandler(async (req: IRequestWithOrganizationAnnotations, res: Response, next: NextFunction) => { // No-op mostly, since ensureAnnotations precedes return res.json({ annotations: req.annotations, - }); + }) as unknown as void; }) ); @@ -127,17 +133,17 @@ function addChangeNote( router.put( '/property/:propertyName', - asyncHandler(async (req: IRequestWithOrganizationAnnotations, res, next) => { + asyncHandler(async (req: IRequestWithOrganizationAnnotations, res: Response, next: NextFunction) => { const { annotations } = req; const providers = getProviders(req); const activeContext = (req.individualContext || req.apiContext) as IndividualContext; const changes: IOrganizationAnnotationChange[] = []; const newValue = req.body.value as string; if (!newValue) { - return next(jsonError('body.value required', 400)); + return next(CreateError.InvalidParameters('body.value required')); } if (typeof newValue !== 'string') { - return next(jsonError('body.value must be a string value', 400)); + return next(CreateError.InvalidParameters('body.value must be a string value')); } const propertyName = req.params.propertyName as string; const currentPropertyValue = annotations.properties[propertyName] || null; @@ -148,13 +154,13 @@ router.put( return res.json({ annotations, updated, - }); + }) as unknown as void; }) ); router.delete( '/property/:propertyName', - asyncHandler(async (req: IRequestWithOrganizationAnnotations, res, next) => { + asyncHandler(async (req: IRequestWithOrganizationAnnotations, res: Response, next: NextFunction) => { const { annotations } = req; const providers = getProviders(req); const activeContext = (req.individualContext || req.apiContext) as IndividualContext; @@ -162,7 +168,7 @@ router.delete( const propertyName = req.params.propertyName as string; const currentPropertyValue = annotations.properties[propertyName] || null; if (annotations.properties[propertyName] === undefined) { - return next(jsonError(`property ${propertyName} is not set`, 400)); + return next(CreateError.InvalidParameters(`property ${propertyName} is not set`)); } delete annotations.properties[propertyName]; addChangeNote( @@ -177,7 +183,7 @@ router.delete( return res.json({ annotations, updated, - }); + }) as unknown as void; }) ); @@ -185,14 +191,14 @@ router.delete( router.put( '/feature/:flag', - asyncHandler(async (req: IRequestWithOrganizationAnnotations, res, next) => { + asyncHandler(async (req: IRequestWithOrganizationAnnotations, res: Response, next: NextFunction) => { const { annotations } = req; const providers = getProviders(req); const activeContext = (req.individualContext || req.apiContext) as IndividualContext; const changes: IOrganizationAnnotationChange[] = []; const flag = req.params.flag as string; if (annotations.features.includes(flag)) { - return next(jsonError(`The feature flag ${flag} is already present`, 400)); + return next(CreateError.InvalidParameters(`The feature flag ${flag} is already present`)); } annotations.features.push(flag); addChangeNote( @@ -207,20 +213,20 @@ router.put( return res.json({ annotations, updated, - }); + }) as unknown as void; }) ); router.delete( '/feature/:flag', - asyncHandler(async (req: IRequestWithOrganizationAnnotations, res, next) => { + asyncHandler(async (req: IRequestWithOrganizationAnnotations, res: Response, next: NextFunction) => { const { annotations } = req; const providers = getProviders(req); const activeContext = (req.individualContext || req.apiContext) as IndividualContext; const changes: IOrganizationAnnotationChange[] = []; const flag = req.params.flag as string; if (!annotations.features.includes(flag)) { - return next(jsonError(`The feature flag ${flag} is not set`, 400)); + return next(CreateError.InvalidParameters(`The feature flag ${flag} is not set`)); } annotations.features = annotations.features.filter((f) => f !== flag); addChangeNote( @@ -235,7 +241,7 @@ router.delete( return res.json({ annotations, updated, - }); + }) as unknown as void; }) ); @@ -243,7 +249,7 @@ router.delete( router.patch( '/', - asyncHandler(async (req: IRequestWithOrganizationAnnotations, res, next) => { + asyncHandler(async (req: IRequestWithOrganizationAnnotations, res: Response, next: NextFunction) => { const { annotations } = req; const providers = getProviders(req); const activeContext = (req.individualContext || req.apiContext) as IndividualContext; @@ -267,7 +273,7 @@ router.patch( return res.json({ annotations, updated, - }); + }) as unknown as void; }) ); @@ -290,7 +296,7 @@ async function applyPatch( // features, properties // flag -router.use('*', (req, res, next) => { +router.use('*', (req, res: Response, next: NextFunction) => { return next(jsonError('no API or function available within the organization annotations route', 404)); }); diff --git a/api/client/organization/index.ts b/api/client/organization/index.ts index 3acf46701..6cc49a116 100644 --- a/api/client/organization/index.ts +++ b/api/client/organization/index.ts @@ -3,13 +3,13 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; import { ReposAppRequest } from '../../../interfaces'; import { jsonError } from '../../../middleware'; import getCompanySpecificDeployment from '../../../middleware/companySpecificDeployment'; -import { getProviders } from '../../../transitional'; +import { getProviders } from '../../../lib/transitional'; import { blockIfUnmanagedOrganization, IReposAppRequestWithOrganizationManagementType, @@ -32,14 +32,14 @@ router.get( asyncHandler(async (req: IReposAppRequestWithOrganizationManagementType, res) => { const { organization, organizationProfile, organizationManagementType } = req; if (organizationManagementType === OrganizationManagementType.Unmanaged) { - return res.json(organizationProfile); + return res.json(organizationProfile) as unknown as void; } const entity = organization.getEntity(); if (entity) { - return res.json(entity); + return res.json(entity) as unknown as void; } const details = await organization.getDetails(); - return res.json(details); + return res.json(details) as unknown as void; }) ); @@ -63,19 +63,21 @@ asClientJson() { */ router.get( '/', - asyncHandler(async (req: IReposAppRequestWithOrganizationManagementType, res, next) => { - const { organization, organizationProfile, organizationManagementType } = req; - if (organizationManagementType === OrganizationManagementType.Unmanaged) { + asyncHandler( + async (req: IReposAppRequestWithOrganizationManagementType, res: Response, next: NextFunction) => { + const { organization, organizationProfile, organizationManagementType } = req; + if (organizationManagementType === OrganizationManagementType.Unmanaged) { + return res.json({ + managementType: req.organizationManagementType, + id: organizationProfile.id, + }) as unknown as void; + } return res.json({ managementType: req.organizationManagementType, - id: organizationProfile.id, - }); + ...organization.asClientJson(), + }) as unknown as void; } - return res.json({ - managementType: req.organizationManagementType, - ...organization.asClientJson(), - }); - }) + ) ); router.use('/annotations', routeAnnotations); @@ -93,7 +95,7 @@ router.get('/newRepoBanner', (req: ReposAppRequest, res) => { return res.json({ newRepositoriesOffline }); }); -router.use('*', (req, res, next) => { +router.use('*', (req, res: Response, next: NextFunction) => { return next(jsonError('no API or function available', 404)); }); diff --git a/api/client/organization/newRepoMetadata.ts b/api/client/organization/newRepoMetadata.ts index 4761fbb8e..4133cb2b5 100644 --- a/api/client/organization/newRepoMetadata.ts +++ b/api/client/organization/newRepoMetadata.ts @@ -3,7 +3,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; import { jsonError } from '../../../middleware/jsonError'; @@ -13,7 +13,7 @@ const router: Router = Router(); router.get( '/', - asyncHandler(async (req: ReposAppRequest, res, next) => { + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { const { organization } = req; const metadata = organization.getRepositoryCreateMetadata(); res.json(metadata); @@ -22,7 +22,7 @@ router.get( router.get( '/byProjectReleaseType', - asyncHandler(async (req: ReposAppRequest, res, next) => { + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { const { organization } = req; const options = { projectType: req.query.projectType, @@ -32,7 +32,7 @@ router.get( }) ); -router.use('*', (req, res, next) => { +router.use('*', (req, res: Response, next: NextFunction) => { return next(jsonError('no API or function available within this path', 404)); }); diff --git a/api/client/organization/people.ts b/api/client/organization/people.ts index f8f39743a..5d75ab5c4 100644 --- a/api/client/organization/people.ts +++ b/api/client/organization/people.ts @@ -3,11 +3,11 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; import { jsonError } from '../../../middleware'; -import { getProviders } from '../../../transitional'; +import { getProviders } from '../../../lib/transitional'; import LeakyLocalCache, { getLinksLightCache } from '../leakyLocalCache'; import JsonPager from '../jsonPager'; import { @@ -118,7 +118,7 @@ export async function equivalentLegacyPeopleSearch(req: ReposAppRequest, options router.get( '/', - asyncHandler(async (req: ReposAppRequest, res, next) => { + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { const pager = new JsonPager(req, res); try { const searcher = await equivalentLegacyPeopleSearch(req); @@ -142,7 +142,7 @@ router.get( }) ); -router.use('*', (req, res, next) => { +router.use('*', (req, res: Response, next: NextFunction) => { return next(jsonError('no API or function available within this people list', 404)); }); diff --git a/api/client/organization/repo.ts b/api/client/organization/repo.ts index 4ad38e634..635105e2d 100644 --- a/api/client/organization/repo.ts +++ b/api/client/organization/repo.ts @@ -3,36 +3,32 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; import { jsonError } from '../../../middleware'; -import { ErrorHelper, getProviders } from '../../../transitional'; -import { Repository } from '../../../business'; +import { CreateError, ErrorHelper, getProviders } from '../../../lib/transitional'; import { IndividualContext } from '../../../business/user'; -import NewRepositoryLockdownSystem from '../../../features/newRepositories/newRepositoryLockdown'; +import NewRepositoryLockdownSystem from '../../../business/features/newRepositories/newRepositoryLockdown'; import { AddRepositoryPermissionsToRequest, getContextualRepositoryPermissions, } from '../../../middleware/github/repoPermissions'; -import { renameRepositoryDefaultBranchEndToEnd } from '../../../routes/org/repos'; import getCompanySpecificDeployment from '../../../middleware/companySpecificDeployment'; import RouteRepoPermissions from './repoPermissions'; import { - ReposAppRequest, LocalApiRepoAction, getRepositoryMetadataProvider, NoCacheNoBackground, + GitHubRepositoryVisibility, } from '../../../interfaces'; +import { RequestWithRepo } from '../../../middleware/business/repository'; -type RequestWithRepo = ReposAppRequest & { - repository: Repository; -}; - -enum ArchivalAction { +enum RepositoryChangeAction { Archive, UnArchive, + Privatize, } const router: Router = Router(); @@ -44,15 +40,15 @@ router.use('/permissions', RouteRepoPermissions); router.get( '/', - asyncHandler(async (req: RequestWithRepo, res, next) => { + asyncHandler(async (req: RequestWithRepo, res: Response, next: NextFunction) => { const { repository } = req; try { await repository.getDetails({ backgroundRefresh: false }); const clone = Object.assign({}, repository.getEntity()); - delete clone.temp_clone_token; // never share this back - delete clone.cost; + delete (clone as any).temp_clone_token; // never share this back + delete (clone as any).cost; - return res.json(repository.getEntity()); + return res.json(repository.getEntity()) as unknown as void; } catch (repoError) { if (ErrorHelper.IsNotFound(repoError)) { // // Attempt fallback by ID (?) @@ -64,7 +60,7 @@ router.get( router.get( '/exists', - asyncHandler(async (req: RequestWithRepo, res, next) => { + asyncHandler(async (req: RequestWithRepo, res: Response, next: NextFunction) => { let exists = false; let name: string = undefined; const { repository } = req; @@ -81,58 +77,85 @@ router.get( } } } catch (repoError) {} - return res.json({ exists, name }); + return res.json({ exists, name }) as unknown as void; }) ); -router.patch( - '/renameDefaultBranch', - asyncHandler(AddRepositoryPermissionsToRequest), - asyncHandler(async function (req: RequestWithRepo, res, next) { - const providers = getProviders(req); - const activeContext = (req.individualContext || req.apiContext) as IndividualContext; - const repoPermissions = getContextualRepositoryPermissions(req); - const targetBranchName = req.body.default_branch; +router.get( + '/archived', + asyncHandler(async (req: RequestWithRepo, res: Response, next: NextFunction) => { const { repository } = req; try { - const result = await renameRepositoryDefaultBranchEndToEnd( - providers, - activeContext, - repoPermissions, - repository, - targetBranchName, - true /* wait for refresh before sending response */ - ); - return res.json(result); + await repository.getDetails(); + const data = { + archivedAt: null, + }; + if (repository?.archived) { + const archivedAt = await repository.getArchivedAt(); + if (archivedAt) { + data.archivedAt = archivedAt.toISOString(); + } + } + return res.json(data) as unknown as void; } catch (error) { - return next(jsonError(error)); + return next(error); } }) ); +router.post( + '/privatize', + asyncHandler(AddRepositoryPermissionsToRequest), + asyncHandler(RepositoryStateChangeHandler.bind(null, RepositoryChangeAction.Privatize)) +); router.post( '/archive', asyncHandler(AddRepositoryPermissionsToRequest), - asyncHandler(archiveUnArchiveRepositoryHandler.bind(null, ArchivalAction.Archive)) + asyncHandler(RepositoryStateChangeHandler.bind(null, RepositoryChangeAction.Archive)) ); router.post( '/unarchive', asyncHandler(AddRepositoryPermissionsToRequest), - asyncHandler(archiveUnArchiveRepositoryHandler.bind(null, ArchivalAction.UnArchive)) + asyncHandler(RepositoryStateChangeHandler.bind(null, RepositoryChangeAction.UnArchive)) ); -async function archiveUnArchiveRepositoryHandler(action: ArchivalAction, req: RequestWithRepo, res, next) { +async function RepositoryStateChangeHandler( + action: RepositoryChangeAction, + req: RequestWithRepo, + res: Response, + next: NextFunction +) { const activeContext = (req.individualContext || req.apiContext) as IndividualContext; const providers = getProviders(req); const { insights } = providers; const repoPermissions = getContextualRepositoryPermissions(req); - const phrase = action === ArchivalAction.Archive ? 'archive' : 'unarchive'; + let phrase: string = null; + let insightsPrefix: string = null; + let localAction: LocalApiRepoAction = null; + switch (action) { + case RepositoryChangeAction.Archive: + phrase = 'archive'; + insightsPrefix = 'ArchiveRepo'; + localAction = LocalApiRepoAction.Archive; + break; + case RepositoryChangeAction.UnArchive: + phrase = 'unarchive'; + insightsPrefix = 'UnArchiveRepo'; + localAction = LocalApiRepoAction.UnArchive; + break; + case RepositoryChangeAction.Privatize: + phrase = 'privatize'; + insightsPrefix = 'PrivatizeRepo'; + localAction = LocalApiRepoAction.Privatize; + break; + default: + return next(jsonError('Invalid action', 400)); + } const completedPhrase = `${phrase}d`; if (!repoPermissions.allowAdministration) { return next(jsonError(`You do not have permission to ${phrase} this repo`, 403)); } - const insightsPrefix = `${action === ArchivalAction.UnArchive ? 'Un' : ''}ArchiveRepo`; const { repository } = req; try { insights?.trackEvent({ @@ -147,16 +170,28 @@ async function archiveUnArchiveRepositoryHandler(action: ArchivalAction, req: Re const currentRepositoryState = deployment?.features?.repositoryActions?.getCurrentRepositoryState ? await deployment.features.repositoryActions.getCurrentRepositoryState(providers, repository) : null; - await (action === ArchivalAction.Archive ? repository.archive() : repository.unarchive()); + switch (action) { + case RepositoryChangeAction.Archive: { + await repository.archive(); + break; + } + case RepositoryChangeAction.UnArchive: { + await repository.unarchive(); + break; + } + case RepositoryChangeAction.Privatize: { + await repository.update({ + visibility: GitHubRepositoryVisibility.Private, + }); + break; + } + default: { + return next(CreateError.InvalidParameters('Invalid action')); + } + } if (deployment?.features?.repositoryActions?.sendActionReceipt) { deployment.features.repositoryActions - .sendActionReceipt( - providers, - activeContext, - repository, - action === ArchivalAction.Archive ? LocalApiRepoAction.Archive : LocalApiRepoAction.UnArchive, - currentRepositoryState - ) + .sendActionReceipt(providers, activeContext, repository, localAction, currentRepositoryState) .then((ok) => {}) .catch(() => {}); } @@ -200,7 +235,7 @@ async function archiveUnArchiveRepositoryHandler(action: ArchivalAction, req: Re router.delete( '/', asyncHandler(AddRepositoryPermissionsToRequest), - asyncHandler(async function (req: RequestWithRepo, res, next) { + asyncHandler(async function (req: RequestWithRepo, res: Response, next: NextFunction) { // NOTE: duplicated code from /routes/org/repos.ts const providers = getProviders(req); const { insights } = providers; @@ -250,7 +285,7 @@ router.delete( }); return res.json({ message: `You deleted: ${repository.full_name}`, - }); + }) as unknown as void; } catch (error) { insights?.trackException({ exception: error }); insights?.trackEvent({ @@ -295,6 +330,7 @@ router.delete( const { operations } = getProviders(req); const repositoryMetadataProvider = getRepositoryMetadataProvider(operations); const lockdownSystem = new NewRepositoryLockdownSystem({ + insights, operations, organization, repository, @@ -306,11 +342,11 @@ router.delete( ); return res.json({ message: `You deleted your repo, ${repository.full_name}.`, - }); + }) as unknown as void; }) ); -router.use('*', (req, res, next) => { +router.use('*', (req, res: Response, next: NextFunction) => { console.warn(req.baseUrl); return next(jsonError('no API or function available within this specific repo', 404)); }); diff --git a/api/client/organization/repoPermissions.ts b/api/client/organization/repoPermissions.ts index b479e8ab8..f5eefaba7 100644 --- a/api/client/organization/repoPermissions.ts +++ b/api/client/organization/repoPermissions.ts @@ -3,7 +3,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; import { jsonError } from '../../../middleware/jsonError'; @@ -19,7 +19,7 @@ const router: Router = Router(); router.get( '/', - asyncHandler(async (req: RequestWithRepo, res, next) => { + asyncHandler(async (req: RequestWithRepo, res: Response, next: NextFunction) => { const { repository, organization } = req; try { const teamPermissions = await repository.getTeamPermissions(); @@ -38,7 +38,7 @@ router.get( collaborators: collaborators.map((c) => c.asJson()), outsideCollaborators: outsideCollaborators.map((oc) => oc.asJson()), memberCollaborators: memberCollaborators.map((oc) => oc.asJson()), - }); + }) as unknown as void; } catch (error) { return next(jsonError(error)); } diff --git a/api/client/organization/repos.ts b/api/client/organization/repos.ts index 499ec7ac5..2a32f163b 100644 --- a/api/client/organization/repos.ts +++ b/api/client/organization/repos.ts @@ -3,23 +3,25 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; import { jsonError } from '../../../middleware'; -import { getProviders } from '../../../transitional'; +import { CreateError, getProviders } from '../../../lib/transitional'; import { Repository } from '../../../business'; -import RouteRepo from './repo'; import JsonPager from '../jsonPager'; import { ReposAppRequest, IProviders } from '../../../interfaces'; -import { sortRepositoriesByNameCaseInsensitive } from '../../../utils'; +import { sortRepositoriesByNameCaseInsensitive } from '../../../lib/utils'; +import { apiMiddlewareRepositoriesToRepository } from '../../../middleware/business/repository'; + +import routeRepo from './repo'; const router: Router = Router(); router.get( '/', - asyncHandler(async (req: ReposAppRequest, res, next) => { + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { const { organization } = req; const providers = getProviders(req); const pager = new JsonPager(req, res); @@ -236,21 +238,10 @@ export async function searchRepos( // --- End of search reimplementation --- -router.use( - '/:repoName', - asyncHandler(async (req: ReposAppRequest, res, next) => { - const { organization } = req; - const { repoName } = req.params; - // does not confirm the name - (req as any).repository = organization.repository(repoName); - return next(); - }) -); - -router.use('/:repoName', RouteRepo); +router.use('/:repoName', asyncHandler(apiMiddlewareRepositoriesToRepository), routeRepo); -router.use('*', (req, res, next) => { - return next(jsonError('no API or function available within this repos endpoint', 404)); +router.use('*', (req, res: Response, next: NextFunction) => { + return next(CreateError.NotFound('no API or function available within org/repos endpoint')); }); export default router; diff --git a/api/client/organization/team.ts b/api/client/organization/team.ts index 20d5925a3..73e44e323 100644 --- a/api/client/organization/team.ts +++ b/api/client/organization/team.ts @@ -3,33 +3,46 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; import { getContextualTeam } from '../../../middleware/github/teamPermissions'; import { jsonError } from '../../../middleware'; -import { getProviders } from '../../../transitional'; +import { getProviders } from '../../../lib/transitional'; import JsonPager from '../jsonPager'; import { getLinksLightCache } from '../leakyLocalCache'; import { equivalentLegacyPeopleSearch } from './people'; import { TeamRepositoryPermission, OrganizationMember, corporateLinkToJson } from '../../../business'; import { ReposAppRequest, TeamJsonFormat, NoCacheNoBackground, ICorporateLink } from '../../../interfaces'; -import { sortRepositoriesByNameCaseInsensitive } from '../../../utils'; +import { sortRepositoriesByNameCaseInsensitive } from '../../../lib/utils'; +import getCompanySpecificDeployment from '../../../middleware/companySpecificDeployment'; const router: Router = Router(); router.get( '/', - asyncHandler(async (req: ReposAppRequest, res, next) => { + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { + const providers = getProviders(req); const team = getContextualTeam(req); - return res.json(team.asJson(TeamJsonFormat.Augmented /* includes corporateMetadata */)); + const format = TeamJsonFormat.Augmented; // includes corporateMetadata + let json = team.asJson(format); + const companySpecific = getCompanySpecificDeployment(); + if (companySpecific?.features?.augmentApiMetadata?.augmentTeamClientJson) { + json = await companySpecific.features.augmentApiMetadata.augmentTeamClientJson( + providers, + team, + json, + format + ); + } + return res.json(json) as unknown as void; }) ); router.get( '/repos', - asyncHandler(async (req: ReposAppRequest, res, next) => { + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { try { const forceRefresh = !!req.query.refresh; const pager = new JsonPager(req, res); @@ -56,7 +69,7 @@ router.get( router.get( '/members', - asyncHandler(async (req: ReposAppRequest, res, next) => { + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { try { const forceRefresh = !!req.query.refresh; const team = getContextualTeam(req); @@ -84,7 +97,7 @@ router.get( router.get( '/maintainers', - asyncHandler(async (req: ReposAppRequest, res, next) => { + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { const { operations } = getProviders(req); try { const forceRefresh = !!req.query.refresh; @@ -108,14 +121,14 @@ router.get( link: corporateLinkToJson(ls.get(Number(maintainer.id))), }; }) - ); + ) as unknown as void; } catch (error) { return next(error); } }) ); -router.use('*', (req, res, next) => { +router.use('*', (req, res: Response, next: NextFunction) => { return next(jsonError('no API or function available for this specific team', 404)); }); diff --git a/api/client/organization/teams.ts b/api/client/organization/teams.ts index eebde0d51..b69b82c84 100644 --- a/api/client/organization/teams.ts +++ b/api/client/organization/teams.ts @@ -3,7 +3,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; import { Organization } from '../../../business/organization'; @@ -24,7 +24,7 @@ const leakyLocalCache = new LeakyLocalCache(); router.use( '/:teamSlug', - asyncHandler(async (req: ReposAppRequest, res, next) => { + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { const { organization } = req; const { teamSlug } = req.params; let team: Team = null; @@ -61,15 +61,19 @@ async function getTeamsForOrganization( router.get( '/', - asyncHandler(async (req: ReposAppRequest, res, next) => { + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { return await getClientApiOrganizationTeamsResponse(req, res, next); }) ); -export async function getClientApiOrganizationTeamsResponse(req: ReposAppRequest, res, next) { +export async function getClientApiOrganizationTeamsResponse( + req: ReposAppRequest, + res: Response, + next: NextFunction +) { const organization = (req.organization || (req as any).aeOrganization) as Organization; if (!organization) { - return next(jsonError('No available organization'), 400); + return next(jsonError('No available organization', 400)); } const pager = new JsonPager(req, res); const q: string = (req.query.q ? (req.query.q as string) : null) || ''; @@ -101,7 +105,7 @@ export async function getClientApiOrganizationTeamsResponse(req: ReposAppRequest } } -router.use('*', (req, res, next) => { +router.use('*', (req, res: Response, next: NextFunction) => { return next(jsonError('no API or function available within this team', 404)); }); diff --git a/api/client/organizations.ts b/api/client/organizations.ts index fac7fcafc..2c26e165c 100644 --- a/api/client/organizations.ts +++ b/api/client/organizations.ts @@ -3,41 +3,41 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; +import throat from 'throat'; -import memoryCache from 'memory-cache'; - -import { jsonError } from '../../middleware'; -import { CreateError, ErrorHelper, getProviders } from '../../transitional'; +import { getIsCorporateAdministrator, jsonError } from '../../middleware'; +import { CreateError, getProviders } from '../../lib/transitional'; import { ReposAppRequest } from '../../interfaces'; import RouteOrganization from './organization'; +import { apiMiddlewareOrganizationsToOrganization } from '../../middleware/business/organization'; +import type { GitHubOrganizationResponseSanitized } from '../../business'; import { - IReposAppRequestWithOrganizationManagementType, - OrganizationManagementType, -} from '../../middleware/business/organization'; - -import { IGitHubOrganizationResponse } from '../../business'; -import { OrganizationAnnotation } from '../../entities/organizationAnnotation'; + OrganizationAnnotation, + OrganizationAnnotationProperty, + getOrganizationAnnotationRestrictedPropertyNames, +} from '../../business/entities/organizationAnnotation'; +import { getOrganizationProfileViaMemoryCache } from '../../middleware/github/ensureOrganizationProfile'; const router: Router = Router(); -type HighlightedOrganization = { - profile: IGitHubOrganizationResponse; +export type OrganizationAnnotationPair = { + profile: GitHubOrganizationResponseSanitized; annotations: OrganizationAnnotation; }; router.get( '/', - asyncHandler(async (req: ReposAppRequest, res, next) => { + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { const { operations } = getProviders(req); try { const orgs = operations.getOrganizations(); const dd = orgs.map((org) => { return org.asClientJson(); }); - return res.json(dd); + return res.json(dd) as unknown as void; } catch (error) { throw jsonError(error, 400); } @@ -46,39 +46,86 @@ router.get( router.get( '/annotations', - asyncHandler(async (req: ReposAppRequest, res, next) => { - const { operations, organizationAnnotationsProvider } = getProviders(req); - const cacheTimeMs = 1000 * 60 * 60 * 24; + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { + const providers = getProviders(req); + const { organizationAnnotationsProvider } = providers; + const projectionQuery = typeof req.query.projection === 'string' ? req.query.projection : undefined; + const isSystemAdministrator = await getIsCorporateAdministrator(req); + // governance filter: a specific value or unset cohort + const governance = + typeof req.query.governance === 'string' ? req.query.governance?.toLowerCase() : undefined; + const filterByGovernance = governance !== undefined; try { - const highlights: HighlightedOrganization[] = []; - const annotations = await organizationAnnotationsProvider.getAllAnnotations(); - for (const annotation of annotations) { + const highlights: OrganizationAnnotationPair[] = []; + let annotations = await organizationAnnotationsProvider.getAllAnnotations(); + if (filterByGovernance) { + annotations = annotations.filter((annotation) => { + const value = annotation?.getProperty(OrganizationAnnotationProperty.Governance); + return governance ? value === governance : !value; + }); + } + const getAnnotationProfile = async (annotation: OrganizationAnnotation) => { try { - const key = `org:profile:${annotation.organizationId}`; - let profile = memoryCache.get(key) as IGitHubOrganizationResponse; - if (!profile) { - const details = await operations.getOrganizationProfileById(Number(annotation.organizationId)); - details.cost && delete details.cost; - details.headers && delete details.headers; - profile = details; - memoryCache.put(key, details, cacheTimeMs); - } - const scrubbedAnnotations = { ...annotation }; - delete scrubbedAnnotations.administratorNotes; - delete scrubbedAnnotations.history; + const profile = await getOrganizationProfileViaMemoryCache(providers, annotation.organizationId); highlights.push({ profile, - annotations: scrubbedAnnotations as OrganizationAnnotation, + annotations: annotation, }); } catch (error) { // we ignore any individual resolution error } + }; + const projections = projectionQuery?.split(','); + if (projections?.length > 0) { + const propertiesToRedact = getOrganizationAnnotationRestrictedPropertyNames(isSystemAdministrator); + if (projections.some((p) => propertiesToRedact.includes(p))) { + throw CreateError.InvalidParameters( + `One or more of the requested projections are not authorized for the current user` + ); + } + } + const parallelRequests = 6; + const throttle = throat(parallelRequests); + await Promise.all(annotations.map((annotation) => throttle(() => getAnnotationProfile(annotation)))); + if (projectionQuery) { + if (projections.length > 1 && !projections.includes('login')) { + throw CreateError.InvalidParameters('When using multiple projections, login must be included'); + } + let projected = highlights.map((highlight) => { + const profile = highlight.profile; + const annotations = highlight.annotations; + const result = {}; + for (const p of projections) { + let value = null; + if (profile[p]) { + value = result[p] = profile[p]; + } else if (annotations?.getProperty(p)) { + value = result[p] = annotations.getProperty(p); + } else if (annotations?.hasFeature(p)) { + value = result[p] = true; + } + if (projections.length === 1) { + return value; + } + } + return result; + }); + if (projections.length === 1 && projected.length >= 1 && typeof projected[0] === 'string') { + projected = projected.sort((a, b) => { + return a.localeCompare(b); + }); + } else if (projections.length > 1) { + projected = projected.sort((a, b) => { + return a['login'].localeCompare(b['login']); + }); + } + return res.json(projected) as unknown as void; } return res.json({ highlights: highlights.sort((a, b) => { return a.profile.login.localeCompare(b.profile.login); }), - }); + }) as unknown as void; } catch (error) { throw jsonError(error, 400); } @@ -87,7 +134,7 @@ router.get( router.get( '/list.txt', - asyncHandler(async (req: ReposAppRequest, res, next) => { + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { const { operations } = getProviders(req); try { const orgs = operations.getOrganizations(); @@ -102,46 +149,10 @@ router.get( }) ); -router.use( - '/:orgName', - asyncHandler(async (req: IReposAppRequestWithOrganizationManagementType, res, next) => { - const { operations } = getProviders(req); - const { orgName } = req.params; - req.organizationName = orgName; - try { - const org = operations.getOrganization(orgName); - if (org) { - req.organizationManagementType = OrganizationManagementType.Managed; - req.organization = org; - return next(); - } - } catch (orgNotFoundError) { - if (!ErrorHelper.IsNotFound(orgNotFoundError)) { - return next(orgNotFoundError); - } - } - try { - const org = operations.getUncontrolledOrganization(orgName); - const details = await org.getDetails(); - details.cost && delete details.cost; - details.headers && delete details.headers; - req.organizationProfile = details; - } catch (orgProfileError) { - if (ErrorHelper.IsNotFound(orgProfileError)) { - return next(CreateError.NotFound(`The organization ${orgName} does not exist`)); - } else { - return next(orgProfileError); - } - } - req.organizationManagementType = OrganizationManagementType.Unmanaged; - return next(); - }) -); - -router.use('/:orgName', RouteOrganization); +router.use('/:orgName', asyncHandler(apiMiddlewareOrganizationsToOrganization), RouteOrganization); -router.use('*', (req: ReposAppRequest, res, next) => { - return next(jsonError('orgs API not found', 404)); +router.use('*', (req: ReposAppRequest, res: Response, next: NextFunction) => { + return next(CreateError.NotFound('orgs API not found')); }); export default router; diff --git a/api/client/people.ts b/api/client/people.ts index c0a2fc12c..41b30e959 100644 --- a/api/client/people.ts +++ b/api/client/people.ts @@ -3,16 +3,16 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; import { corporateLinkToJson } from '../../business'; import { jsonError } from '../../middleware'; -import { ICorporateLink, ReposAppRequest } from '../../interfaces'; +import { type GitHubSimpleAccount, type ICorporateLink, ReposAppRequest } from '../../interfaces'; import JsonPager from './jsonPager'; import getCompanySpecificDeployment from '../../middleware/companySpecificDeployment'; -import RouteGetPerson from './person'; +import { getPerson as routeGetPerson } from './person'; import { equivalentLegacyPeopleSearch } from './peopleSearch'; const router: Router = Router(); @@ -20,34 +20,28 @@ const router: Router = Router(); const deployment = getCompanySpecificDeployment(); deployment?.routes?.api?.people && deployment.routes.api.people(router); -interface ISimpleAccount { - login: string; - avatar_url: string; - id: number; -} - export interface ICrossOrganizationMemberResponse { - account: ISimpleAccount; + account: GitHubSimpleAccount; link?: ICorporateLink; organizations: string[]; } export interface ICrossOrganizationSearchedMember { id: number; - account: ISimpleAccount; + account: GitHubSimpleAccount; link?: ICorporateLink; orgs: IOrganizationMembershipAccount; } interface IOrganizationMembershipAccount { - [id: string]: ISimpleAccount; + [id: string]: GitHubSimpleAccount; } -router.get('/:login', RouteGetPerson); +router.get('/:login', routeGetPerson); router.get( '/', - asyncHandler(async (req: ReposAppRequest, res, next) => { + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { const pager = new JsonPager(req, res); try { const searcher = await equivalentLegacyPeopleSearch(req); @@ -73,7 +67,7 @@ router.get( }) ); -router.use('*', (req, res, next) => { +router.use('*', (req, res: Response, next: NextFunction) => { return next(jsonError('no API or function available within this cross-organization people list', 404)); }); diff --git a/api/client/peopleSearch.ts b/api/client/peopleSearch.ts index 1c55e6d16..82ae5c27f 100644 --- a/api/client/peopleSearch.ts +++ b/api/client/peopleSearch.ts @@ -5,7 +5,7 @@ import { Organization, MemberSearch, ICrossOrganizationMembersResult, Operations } from '../../business'; import { ReposAppRequest } from '../../interfaces'; -import { getProviders } from '../../transitional'; +import { getProviders } from '../../lib/transitional'; import LeakyLocalCache, { getLinksLightCache } from './leakyLocalCache'; // BAD PRACTICE: leaky local cache diff --git a/api/client/person.ts b/api/client/person.ts index 913658945..61369456a 100644 --- a/api/client/person.ts +++ b/api/client/person.ts @@ -4,13 +4,15 @@ // import asyncHandler from 'express-async-handler'; +import { NextFunction, Response } from 'express'; + import { ReposAppRequest, AccountJsonFormat } from '../../interfaces'; import { IGraphEntry } from '../../lib/graphProvider'; import { jsonError } from '../../middleware'; -import { getProviders } from '../../transitional'; +import { CreateError, ErrorHelper, getProviders } from '../../lib/transitional'; -export default asyncHandler(async (req: ReposAppRequest, res, next) => { +const getPerson = asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { const providers = getProviders(req); const { operations, queryCache, graphProvider } = providers; const login = req.params.login as string; @@ -70,8 +72,14 @@ export default asyncHandler(async (req: ReposAppRequest, res, next) => { json, { corporateEntry } ); - return res.json(combined); + return res.json(combined) as unknown as void; } catch (error) { - return next(jsonError(`login ${login} error: ${error}`, 500)); + return next( + ErrorHelper.IsNotFound(error) + ? error + : CreateError.InvalidParameters(`Invalid issue retrieving user ${login}: ${error.message}`) + ); } }); + +export { getPerson }; diff --git a/api/client/repos.ts b/api/client/repos.ts index ae426e7df..ca1675f2a 100644 --- a/api/client/repos.ts +++ b/api/client/repos.ts @@ -3,13 +3,13 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; import { Repository } from '../../business'; import { ReposAppRequest } from '../../interfaces'; import { jsonError } from '../../middleware'; -import { getProviders } from '../../transitional'; +import { getProviders } from '../../lib/transitional'; import JsonPager from './jsonPager'; import { RepositorySearchSortOrder, searchRepos } from './organization/repos'; @@ -17,7 +17,7 @@ const router: Router = Router(); router.get( '/', - asyncHandler(async (req: ReposAppRequest, res, next) => { + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { const providers = getProviders(req); const pager = new JsonPager(req, res); const searchOptions = { @@ -39,7 +39,7 @@ router.get( }) ); -router.use('*', (req, res, next) => { +router.use('*', (req, res: Response, next: NextFunction) => { return next(jsonError('no API or function available within this cross-organization repps list', 404)); }); diff --git a/api/client/session.ts b/api/client/session.ts index e0b128e99..35f98dd2a 100644 --- a/api/client/session.ts +++ b/api/client/session.ts @@ -3,11 +3,11 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import { jsonError } from '../../middleware/jsonError'; import { IAppSession, ReposAppRequest } from '../../interfaces'; -import { getProviders } from '../../transitional'; +import { getProviders } from '../../lib/transitional'; const router: Router = Router(); @@ -42,7 +42,7 @@ router.post('/github', (req: ReposAppRequest, res) => { res.end(); }); -router.use('*', (req: ReposAppRequest, res, next) => { +router.use('*', (req: ReposAppRequest, res: Response, next: NextFunction) => { return next(jsonError('API or route not found', 404)); }); diff --git a/api/client/teams.ts b/api/client/teams.ts index b2c863291..089fa240b 100644 --- a/api/client/teams.ts +++ b/api/client/teams.ts @@ -3,7 +3,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; import { Operations, Team } from '../../business'; @@ -13,7 +13,7 @@ import { TeamJsonFormat, } from '../../interfaces'; import { jsonError } from '../../middleware'; -import { getProviders } from '../../transitional'; +import { getProviders } from '../../lib/transitional'; import JsonPager from './jsonPager'; const router: Router = Router(); @@ -40,7 +40,7 @@ async function getCrossOrganizationTeams(operations: Operations): Promise { + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { const { operations } = getProviders(req); const pager = new JsonPager(req, res); const q: string = (req.query.q ? (req.query.q as string) : null) || ''; @@ -71,7 +71,7 @@ router.get( }) ); -router.use('*', (req, res, next) => { +router.use('*', (req, res: Response, next: NextFunction) => { return next(jsonError('no API or function available within this cross-organization teams list', 404)); }); diff --git a/api/client/users.ts b/api/client/users.ts new file mode 100644 index 000000000..940789452 --- /dev/null +++ b/api/client/users.ts @@ -0,0 +1,35 @@ +// +// Copyright (c) Microsoft. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +import { NextFunction, Response, Router } from 'express'; +import asyncHandler from 'express-async-handler'; + +import { ReposAppRequest, AccountJsonFormat } from '../../interfaces'; +import { CreateError, getProviders } from '../../lib/transitional'; + +const router: Router = Router(); + +router.get( + '/:login', + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { + const { operations } = getProviders(req); + const login = req.params.login as string; + try { + if (!login) { + throw CreateError.ParameterRequired('login'); + } + const accountInfo = await operations.getAccountByUsername(login); + return res.json(accountInfo.asJson(AccountJsonFormat.GitHubExtended)) as any as void; + } catch (error) { + return next(error); + } + }) +); + +router.use('*', (req, res: Response, next: NextFunction) => { + return next(CreateError.NotFound('/users: no API found')); +}); + +export default router; diff --git a/api/createRepo.ts b/api/createRepo.ts index c986bc293..585949c25 100644 --- a/api/createRepo.ts +++ b/api/createRepo.ts @@ -17,9 +17,9 @@ import { ICustomizedNewRepositoryLogic, INewRepositoryContext, splitSemiColonCommas, -} from '../transitional'; +} from '../lib/transitional'; import { Organization, Repository } from '../business'; -import { RepositoryMetadataEntity } from '../entities/repositoryMetadata/repositoryMetadata'; +import { RepositoryMetadataEntity } from '../business/entities/repositoryMetadata/repositoryMetadata'; import RenderHtmlMail from '../lib/emailRender'; import { @@ -29,7 +29,7 @@ import { } from '../routes/org/repoWorkflowEngine'; import { IMailProvider } from '../lib/mailProvider'; import { IndividualContext } from '../business/user'; -import NewRepositoryLockdownSystem from '../features/newRepositories/newRepositoryLockdown'; +import NewRepositoryLockdownSystem from '../business/features/newRepositories/newRepositoryLockdown'; import { ICreateRepositoryResult, ICorporateLink, @@ -176,7 +176,7 @@ export async function CreateRepository( try { createResult = await organization.createRepository(parameters.name, parameters); if (createResult && createResult.repository) { - repository = organization.repositoryFromEntity(createResult.repository); + repository = organization.repositoryFromEntity(createResult.repository.getEntity()); } } catch (error) { providers.insights?.trackEvent({ @@ -624,14 +624,22 @@ async function sendEmail( req.insights.trackException({ exception: renderError, properties: { - content: contentOptions, + correlationId, + existingRepoId, + orgName: repository.organization.name, + repoName: repository.name, + results: repoCreateResults, eventName: 'ApiRepoCreateMailRenderFailure', }, }); throw renderError; } const customData = { - content: contentOptions, + correlationId, + existingRepoId, + orgName: repository.organization.name, + repoName: repository.name, + results: repoCreateResults, receipt: null, eventName: undefined, }; diff --git a/api/extension.ts b/api/extension.ts index c339ab89a..4e9e2f1b0 100644 --- a/api/extension.ts +++ b/api/extension.ts @@ -3,25 +3,25 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Response, Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; const router: Router = Router(); -import { getProviders } from '../transitional'; +import { getProviders } from '../lib/transitional'; import { setIdentity } from '../middleware/business/authentication'; -import { AddLinkToRequest } from '../middleware/links'; +import { AddLinkToRequest } from '../middleware/business/links'; import { jsonError } from '../middleware'; import { apiContextMiddleware } from '../middleware/business/setContext'; -import { ILocalExtensionKeyProvider } from '../entities/localExtensionKey'; -import { LocalExtensionKey } from '../entities/localExtensionKey/localExtensionKey'; +import { ILocalExtensionKeyProvider } from '../business/entities/localExtensionKey'; +import { LocalExtensionKey } from '../business/entities/localExtensionKey/localExtensionKey'; import { IApiRequest } from '../middleware/apiReposAuth'; -import { PersonalAccessToken } from '../entities/token/token'; +import { PersonalAccessToken } from '../business/entities/token/token'; const thisApiScopeName = 'extension'; -interface IExtensionResponse extends Response { +type ExtensionResponse = Response & { localKey?: any; -} +}; interface IConnectionInformation { link?: any; @@ -29,7 +29,7 @@ interface IConnectionInformation { auth?: any; } -router.use(function (req: IApiRequest, res, next) { +router.use(function (req: IApiRequest, res: Response, next: NextFunction) { const token = req.apiKeyToken; if (!token.scopes) { return next(jsonError('The key is not authorized for specific APIs', 403)); @@ -40,7 +40,7 @@ router.use(function (req: IApiRequest, res, next) { return next(); }); -function overwriteUserContext(req: IApiRequest, res, next) { +function overwriteUserContext(req: IApiRequest, res: Response, next: NextFunction) { const token = req.apiKeyToken; const corporateId = token.corporateId; if (!corporateId) { @@ -121,7 +121,7 @@ router.get('/', (req: IApiRequest, res) => { router.get( '/metadata', asyncHandler(getLocalEncryptionKeyMiddleware), - (req: IApiRequest, res: IExtensionResponse) => { + (req: IApiRequest, res: ExtensionResponse) => { const apiContext = req.apiContext; const localKey = res.localKey; @@ -186,7 +186,11 @@ function getSanitizedOrganizations(operations) { return value; } -async function getLocalEncryptionKeyMiddleware(req: IApiRequest, res, next): Promise { +async function getLocalEncryptionKeyMiddleware( + req: IApiRequest, + res: ExtensionResponse, + next: NextFunction +): Promise { const providers = getProviders(req); const localExtensionKeyProvider = providers.localExtensionKeyProvider; const apiKeyToken = req.apiKeyToken; @@ -250,7 +254,7 @@ async function getOrCreateLocalEncryptionKey( return await createLocalEncryptionKey(insights, localExtensionKeyProvider, corporateId); } -router.use('*', (req, res, next) => { +router.use('*', (req, res: Response, next: NextFunction) => { return next(jsonError('API not found', 404)); }); diff --git a/api/index.ts b/api/index.ts index a26d7f502..a4f5c58ba 100644 --- a/api/index.ts +++ b/api/index.ts @@ -3,13 +3,13 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; const router: Router = Router(); import cors from 'cors'; -import { CreateError, getProviders } from '../transitional'; +import { CreateError, getProviders } from '../lib/transitional'; import { jsonError } from '../middleware'; import { IApiRequest } from '../middleware/apiReposAuth'; @@ -17,8 +17,9 @@ import { IApiRequest } from '../middleware/apiReposAuth'; import apiExtension from './extension'; import apiWebhook from './webhook'; import apiPeople from './people'; +import apiNews from './client/news'; -import AadApiAuthentication, { requireAadApiAuthorizedScope } from '../middleware/apiAad'; +import aadApiAuthentication, { requireAadApiAuthorizedScope } from '../middleware/apiAad'; import AzureDevOpsAuthenticationMiddleware from '../middleware/apiVstsAuth'; import ReposApiAuthentication from '../middleware/apiReposAuth'; import { CreateRepository, CreateRepositoryEntrypoint } from './createRepo'; @@ -36,7 +37,7 @@ function isClientRoute(req: ReposAppRequest) { router.use('/webhook', apiWebhook); -router.use((req: IApiRequest, res, next) => { +router.use((req: IApiRequest, res: Response, next: NextFunction) => { if (isClientRoute(req)) { // The frontend client routes are hooked into Express after // the session middleware. The client route does not require @@ -67,15 +68,16 @@ router.use((req: IApiRequest, res, next) => { // AUTHENTICATION: VSTS or repos //----------------------------------------------------------------------------- const multipleProviders = supportMultipleAuthProviders([ - AadApiAuthentication, + aadApiAuthentication, ReposApiAuthentication, AzureDevOpsAuthenticationMiddleware, ]); -const aadAndCustomProviders = supportMultipleAuthProviders([AadApiAuthentication, ReposApiAuthentication]); +const aadAndCustomProviders = supportMultipleAuthProviders([aadApiAuthentication, ReposApiAuthentication]); router.use('/people', cors(), multipleProviders, apiPeople); router.use('/extension', cors(), multipleProviders, apiExtension); +router.use('/news', cors(), aadApiAuthentication, requireAadApiAuthorizedScope('news'), apiNews); //----------------------------------------------------------------------------- // AUTHENTICATION: AAD or repos (specific to this app) @@ -90,8 +92,8 @@ router.post('/:org/repos', aadAndCustomProviders); router.post( '/:org/repos', - requireAadApiAuthorizedScope('createRepo'), - function (req: IApiRequest, res, next) { + requireAadApiAuthorizedScope(['repo/create', 'createRepo']), + function (req: IApiRequest, res: Response, next: NextFunction) { const orgName = req.params.org; if (!req.apiKeyToken.organizationScopes) { return next(jsonError('There is a problem with the key configuration (no organization scopes)', 412)); @@ -116,7 +118,7 @@ router.post( router.post( '/:org/repos', - asyncHandler(async function (req: ReposAppRequest, res, next) { + asyncHandler(async function (req: ReposAppRequest, res: Response, next: NextFunction) { const providers = getProviders(req); const organization = req.organization; const convergedObject = Object.assign({}, req.headers); @@ -174,7 +176,7 @@ router.post( response: JSON.stringify(repoCreateResponse), }, }); - return res.json(repoCreateResponse); + return res.json(repoCreateResponse) as unknown as void; } catch (error) { const data = { ...convergedObject }; data.error = error.message; @@ -185,7 +187,7 @@ router.post( }) ); -router.use((req: IApiRequest, res, next) => { +router.use((req: IApiRequest, res: Response, next: NextFunction) => { if (isClientRoute(req)) { // The frontend client routes are hooked into Express after // the session middleware. The client route does not require diff --git a/api/jsonErrorHandler.ts b/api/jsonErrorHandler.ts index 50915b863..9fa874805 100644 --- a/api/jsonErrorHandler.ts +++ b/api/jsonErrorHandler.ts @@ -3,9 +3,10 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { getProviders } from '../transitional'; +import { NextFunction, Response } from 'express'; +import { getProviders } from '../lib/transitional'; -export default function JsonErrorHandler(err, req, res, next) { +export default function JsonErrorHandler(err, req, res: Response, next: NextFunction) { if (err && err['json']) { // jsonError objects should bubble up like before return next(err); diff --git a/api/people/index.ts b/api/people/index.ts index a724df813..2a02f017e 100644 --- a/api/people/index.ts +++ b/api/people/index.ts @@ -3,7 +3,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import { json404 } from '../../middleware/jsonError'; diff --git a/api/people/link.ts b/api/people/link.ts index d92825a8a..3202e00f3 100644 --- a/api/people/link.ts +++ b/api/people/link.ts @@ -3,7 +3,9 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { getProviders } from '../../transitional'; +import { NextFunction, Response } from 'express'; + +import { getProviders } from '../../lib/transitional'; import { jsonError } from '../../middleware'; import { IApiRequest } from '../../middleware/apiReposAuth'; import { ICorporateLink, LinkOperationSource } from '../../interfaces'; @@ -15,7 +17,7 @@ const supportedApiVersions = new Set([ '2019-10-01', ]); -export default async function postLinkApi(req: IApiRequest, res, next) { +export default async function postLinkApi(req: IApiRequest, res: Response, next: NextFunction) { const providers = getProviders(req); const { operations } = providers; const token = req.apiKeyToken; diff --git a/api/people/links.ts b/api/people/links.ts index 5332bab1a..3ea4caa59 100644 --- a/api/people/links.ts +++ b/api/people/links.ts @@ -3,16 +3,16 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; import { jsonError } from '../../middleware'; import { ICrossOrganizationMembersResult, MemberSearch, Operations } from '../../business'; -import { ICorporateLink } from '../../interfaces'; +import { ICorporateLink, VoidedExpressRoute } from '../../interfaces'; import { IApiRequest } from '../../middleware/apiReposAuth'; import postLinkApi from './link'; -import { ErrorHelper, getProviders } from '../../transitional'; -import { wrapError } from '../../utils'; +import { CreateError, ErrorHelper, getProviders } from '../../lib/transitional'; +import { wrapError } from '../../lib/utils'; const router: Router = Router(); @@ -26,7 +26,7 @@ const extendedLinkApiVersions = [ '2019-02-01', ]; -router.use(function (req: IApiRequest, res, next) { +router.use(function (req: IApiRequest, res: Response, next: NextFunction) { const token = req.apiKeyToken; if (!token.scopes) { return next(jsonError('The key is not authorized for specific APIs', 401)); @@ -37,11 +37,11 @@ router.use(function (req: IApiRequest, res, next) { return next(); }); -router.post('/', asyncHandler(postLinkApi)); +router.post('/', asyncHandler(postLinkApi as VoidedExpressRoute)); router.get( '/', - asyncHandler(async (req: IApiRequest, res, next) => { + asyncHandler(async (req: IApiRequest, res: Response, next: NextFunction) => { const { operations } = getProviders(req); const skipOrganizations = req.query.showOrganizations !== undefined && !!req.query.showOrganizations; const showTimestamps = req.query.showTimestamps !== undefined && req.query.showTimestamps === 'true'; @@ -54,9 +54,11 @@ router.get( router.get( '/:linkid', - asyncHandler(async (req: IApiRequest, res, next) => { + asyncHandler(async (req: IApiRequest, res: Response, next: NextFunction) => { if (unsupportedApiVersions.includes(req.apiVersion)) { - return next(jsonError('This API is not supported by the API version you are using.', 400)); + return next( + CreateError.InvalidParameters('This API is not supported by the API version you are using.') + ); } const linkid = req.params.linkid.toLowerCase(); const { operations } = getProviders(req); @@ -81,31 +83,33 @@ router.get( ); } catch (error) { if (ErrorHelper.IsNotFound(error)) { - return next(jsonError('Could not find the link', 404)); + return next(CreateError.NotFound('Could not find the link')); } else { - return next(jsonError(error, 500)); + return next(CreateError.ServerError(error)); } } req.insights.trackMetric({ name: 'ApiRequestLinkByLinkId', value: 1 }); - return res.json(entry); + return res.json(entry) as unknown as void; } const results = await getAllUsers(req.apiVersion, operations, skipOrganizations, showTimestamps, true); for (let i = 0; i < results.length; i++) { const entry = results[i]; if (entry && entry.id === linkid) { req.insights.trackMetric({ name: 'ApiRequestLinkByLinkId', value: 1 }); - return res.json(entry); + return res.json(entry) as unknown as void; } } - return next(jsonError('Could not find the link', 404)); + return next(CreateError.NotFound('Could not find the link')); }) ); router.get( '/github/:username', - asyncHandler(async (req: IApiRequest, res, next) => { + asyncHandler(async (req: IApiRequest, res: Response, next: NextFunction) => { if (unsupportedApiVersions.includes(req.apiVersion)) { - return next(jsonError('This API is not supported by the API version you are using.', 400)); + return next( + CreateError.InvalidParameters('This API is not supported by the API version you are using.') + ); } const username = req.params.username.toLowerCase(); const { operations } = getProviders(req); @@ -118,9 +122,9 @@ router.get( account = await operations.getAccountByUsername(username); } catch (getAccountError) { if (ErrorHelper.IsNotFound(account)) { - return next(jsonError('Could not find a link for the user', 404)); + return next(CreateError.NotFound('Could not find a link for the user')); } - return next(jsonError(getAccountError, 500)); + return next(CreateError.ServerError(getAccountError)); } try { const entry = await getByThirdPartyId( @@ -131,7 +135,7 @@ router.get( showTimestamps ); req.insights.trackMetric({ name: 'ApiRequestLinkByGitHubUsername', value: 1 }); - return res.json(entry); + return res.json(entry) as unknown as void; } catch (entryError) { return next(jsonError(entryError, ErrorHelper.GetStatus(entryError) || 500)); } @@ -141,16 +145,16 @@ router.get( const entry = results[i]; if (entry && entry.github && entry.github.login.toLowerCase() === username) { req.insights.trackMetric({ name: 'ApiRequestLinkByGitHubUsername', value: 1 }); - return res.json(entry); + return res.json(entry) as unknown as void; } } - return next(jsonError('Could not find a link for the user', 404)); + return next(CreateError.NotFound('Could not find a link for the user')); }) ); router.get( '/aad/userPrincipalName/:upn', - asyncHandler(async (req: IApiRequest, res, next) => { + asyncHandler(async (req: IApiRequest, res: Response, next: NextFunction) => { const upn = req.params.upn; const { operations } = getProviders(req); const skipOrganizations = req.query.showOrganizations !== undefined && !!req.query.showOrganizations; @@ -185,7 +189,7 @@ router.get( userPrincipalName: upn, }, }); - return res.json(r); + return res.json(r) as unknown as void; } const results = await getAllUsers(req.apiVersion, operations, skipOrganizations, showTimestamps); const r = []; @@ -203,18 +207,20 @@ router.get( }, }); if (r.length === 0) { - return next(jsonError('Could not find a link for the user', 404)); + return next(CreateError.NotFound('Could not find a link for the user')); } req.insights.trackMetric({ name: 'ApiRequestLinkByAadUpn', value: 1 }); - return res.json(r); + return res.json(r) as unknown as void; }) ); router.get( '/aad/:id', - asyncHandler(async (req: IApiRequest, res, next) => { + asyncHandler(async (req: IApiRequest, res: Response, next: NextFunction) => { if (req.apiVersion == '2016-12-01') { - return next(jsonError('This API is not supported by the API version you are using.', 400)); + return next( + CreateError.InvalidParameters('This API is not supported by the API version you are using.') + ); } const id = req.params.id; const skipOrganizations = req.query.showOrganizations !== undefined && !!req.query.showOrganizations; @@ -244,7 +250,7 @@ router.get( } } req.insights.trackMetric({ name: 'ApiRequestLinkByAadId', value: 1 }); - return res.json(r); + return res.json(r) as unknown as void; } const results = await getAllUsers(req.apiVersion, operations, skipOrganizations, showTimestamps); const r = []; @@ -258,7 +264,7 @@ router.get( return next(jsonError('Could not find a link for the user', 404)); } req.insights.trackMetric({ name: 'ApiRequestLinkByAadId', value: 1 }); - return res.json(r); + return res.json(r) as unknown as void; }) ); @@ -271,7 +277,6 @@ async function getByThirdPartyId( showLinkIds?: boolean ): Promise { const providers = operations.providers; - const { graphProvider } = providers; let link: ICorporateLink = null; try { link = await providers.linkProvider.getByThirdPartyId(thirdPartyId); @@ -346,7 +351,7 @@ async function getByThirdPartyId( } async function getAllUsers( - apiVersion, + apiVersion: string, operations: Operations, skipOrganizations: boolean, showTimestamps: boolean, diff --git a/api/people/unlink.ts b/api/people/unlink.ts index 3a7aea7d5..dbaa25e72 100644 --- a/api/people/unlink.ts +++ b/api/people/unlink.ts @@ -3,13 +3,13 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; import { ICorporateLink, UnlinkPurpose } from '../../interfaces'; import { jsonError } from '../../middleware'; import { IApiRequest } from '../../middleware/apiReposAuth'; -import { getProviders } from '../../transitional'; +import { getProviders } from '../../lib/transitional'; const router: Router = Router(); @@ -17,7 +17,7 @@ interface ILinksApiRequestWithUnlink extends IApiRequest { unlink?: ICorporateLink; } -router.use(function (req: ILinksApiRequestWithUnlink, res, next) { +router.use(function (req: ILinksApiRequestWithUnlink, res: Response, next: NextFunction) { const token = req.apiKeyToken; if (!token.scopes) { return next(jsonError('The key is not authorized for specific APIs', 401)); @@ -30,7 +30,7 @@ router.use(function (req: ILinksApiRequestWithUnlink, res, next) { router.use( '/github/id/:id', - asyncHandler(async (req: ILinksApiRequestWithUnlink, res, next) => { + asyncHandler(async (req: ILinksApiRequestWithUnlink, res: Response, next: NextFunction) => { const { linkProvider } = getProviders(req); const id = req.params.id; try { @@ -46,11 +46,11 @@ router.use( }) ); -router.use('*', (req: ILinksApiRequestWithUnlink, res, next) => { +router.use('*', (req: ILinksApiRequestWithUnlink, res: Response, next: NextFunction) => { return next(req.unlink ? undefined : jsonError('No link available for operation', 404)); }); -router.delete('*', (req: ILinksApiRequestWithUnlink, res, next) => { +router.delete('*', (req: ILinksApiRequestWithUnlink, res: Response, next: NextFunction) => { const { config, operations } = getProviders(req); const link = req.unlink; let purpose: UnlinkPurpose = null; diff --git a/api/webhook.ts b/api/webhook.ts index a41104162..c7531a8ce 100644 --- a/api/webhook.ts +++ b/api/webhook.ts @@ -3,16 +3,16 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; import moment from 'moment'; import { ReposAppRequest } from '../interfaces'; import { jsonError } from '../middleware'; -import { getProviders, isWebhookIngestionEndpointEnabled } from '../transitional'; +import { getProviders, isWebhookIngestionEndpointEnabled } from '../lib/transitional'; -import OrganizationWebhookProcessor from '../webhooks/organizationProcessor'; +import OrganizationWebhookProcessor from '../business/webhooks/organizationProcessor'; const router: Router = Router(); @@ -21,7 +21,7 @@ interface IRequestWithRaw extends ReposAppRequest { } router.use( - asyncHandler(async (req: IRequestWithRaw, res, next) => { + asyncHandler(async (req: IRequestWithRaw, res: Response, next: NextFunction) => { if (!isWebhookIngestionEndpointEnabled(req)) { return next( jsonError( diff --git a/app.ts b/app.ts deleted file mode 100644 index 6bcc8ce8c..000000000 --- a/app.ts +++ /dev/null @@ -1,156 +0,0 @@ -// -// Copyright (c) Microsoft. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. -// - -import express from 'express'; -import { hostname } from 'os'; - -import { IReposApplication, IReposJob, IReposJobOptions, IReposJobResult } from './interfaces'; - -import configResolver from './lib/config'; -import initialize from './middleware/initialize'; -import { quitInTenSeconds } from './utils'; - -const app = express() as any as IReposApplication; - -import Debug from 'debug'; -Debug.debug('startup')('starting...'); - -app.initializeApplication = initialize.bind(undefined, app, express, __dirname); - -app.initializeJob = function initializeJob(config, configurationError) { - if (config) { - config.isJobInternal = true; - config.skipModules = new Set(['web']); - } else { - console.warn(`Configuration did not resolve successfully`, configurationError); - } - return initialize(app, express, __dirname, config, configurationError); -}; - -async function startup(startupApplication: boolean) { - let painlessConfigResolver = null; - try { - painlessConfigResolver = configResolver(); - } catch (error) { - console.warn('Painless config resolver initialization error:'); - console.error(error); - throw error; - } - let config: any = null; - let configurationError: Error = null; - try { - config = await painlessConfigResolver.resolve(); - } catch (error) { - configurationError = error; - } - - try { - if (startupApplication) { - await app.initializeApplication(config, configurationError); - } else { - await app.initializeJob(config, configurationError); - } - } catch (startupError) { - console.error(`Startup error: ${startupError}`); - process.exit(1); // throw startupError; - } - - return app; -} - -app.startupApplication = startup.bind(null, true); -app.startupJob = startup.bind(null, false); -app.runJob = async function ( - job: (job: IReposJob) => Promise, - options?: IReposJobOptions -): Promise { - options = options || {}; - // TODO: automatically track elapsed job time - const started = new Date(); - if (options.timeoutMinutes) { - setTimeout(() => { - // TODO: insights metric and event, if a prefix exists - console.log(`Kill bit at ${options.timeoutMinutes}m`); - process.exit(1); - }, 1000 * 60 * options.timeoutMinutes); - } - if (options.defaultDebugOutput && !process.env.DEBUG) { - process.env.DEBUG = options.defaultDebugOutput; - } - app.isBackgroundJob = true; - if (options.enableAllGitHubApps) { - app.enableAllGitHubApps = true; - } - try { - await app.startupJob(); - } catch (startupError) { - console.error(`Job startup error before runJob: ${startupError}`); - quitInTenSeconds(false); - return app; - } - if (options.insightsPrefix && app.providers.insights) { - try { - app.providers.insights.trackEvent({ - name: `${options.insightsPrefix}Started`, - properties: { - hostname: hostname(), - }, - }); - } catch (ignoreInsightsError) { - console.error(`insights error: ${ignoreInsightsError}`); - } - } - const jobObject = { - app, - providers: app.providers, - started, - parameters: options && options.parameters ? options.parameters : {}, - args: process.argv.length > 2 ? process.argv.slice(2) : [], - }; - try { - const result = await job.call(null, jobObject); - if (result && result.successProperties && app.providers.insights && options.insightsPrefix) { - try { - app.providers.insights.trackEvent({ - name: `${options.insightsPrefix}Success`, - properties: Object.assign( - { - hostname: hostname(), - }, - result.successProperties - ), - }); - } catch (ignoreInsightsError) { - console.error(`insights error: ${ignoreInsightsError}`); - } - } - } catch (jobError) { - console.error(`The job failed: ${jobError}`); - // by default, let's not show the whole inner error - const simpleError = { ...jobError }; - simpleError?.cause && delete simpleError.cause; - console.dir(simpleError); - quitInTenSeconds(false); - if (options.insightsPrefix && app.providers.insights) { - try { - app.providers.insights.trackException({ - exception: jobError, - properties: { - name: `${options.insightsPrefix}Failure`, - }, - }); - } catch (ignoreInsightsError) { - console.error(`insights error: ${ignoreInsightsError}`); - } - } - return app; - } - // CONSIDER: insights metric for job time - console.log('The job was successful.'); - quitInTenSeconds(true); - return app; -}; - -export default app; diff --git a/bin/www.ts b/bin/www.ts index 8fbd05f5c..af8fe7d7b 100644 --- a/bin/www.ts +++ b/bin/www.ts @@ -9,13 +9,15 @@ import Debug from 'debug'; const debug = Debug.debug('g:server'); const debugInitialization = Debug.debug('startup'); -import app from '../app'; - import http from 'http'; import https from 'https'; import fs from 'fs'; import path from 'path'; +import { createExpressApplication } from '..'; + +const app = createExpressApplication(); + function normalizePort(val) { const port = parseInt(val, 10); diff --git a/business/account.ts b/business/account.ts index 473eb8997..990c52a49 100644 --- a/business/account.ts +++ b/business/account.ts @@ -7,18 +7,18 @@ import _ from 'lodash'; import * as common from './common'; -import { wrapError } from '../utils'; +import { wrapError } from '../lib/utils'; import { corporateLinkToJson } from './corporateLink'; import { Organization } from './organization'; -import { AppPurpose } from './githubApps'; +import { AppPurpose } from '../lib/github/appPurposes'; import { ILinkProvider } from '../lib/linkProviders'; -import { CacheDefault, getMaxAgeSeconds } from '.'; +import { CacheDefault, Operations, getMaxAgeSeconds } from '.'; import { AccountJsonFormat, CoreCapability, ICacheOptions, ICorporateLink, - IGetAuthorizationHeader, + GetAuthorizationHeader, IGitHubAccountDetails, IOperationsInstance, IOperationsLinks, @@ -29,7 +29,7 @@ import { throwIfNotCapable, throwIfNotGitHubCapable, } from '../interfaces'; -import { ErrorHelper } from '../transitional'; +import { ErrorHelper } from '../lib/transitional'; interface IRemoveOrganizationMembershipsResult { error?: IReposError; @@ -46,7 +46,7 @@ const secondaryAccountProperties = []; export class Account { private _operations: IOperationsInstance; - private _getAuthorizationHeader: IGetAuthorizationHeader; + private _getAuthorizationHeader: GetAuthorizationHeader; private _link: ICorporateLink; private _id: number; @@ -70,6 +70,12 @@ export class Account { case AccountJsonFormat.GitHub: { return basic; } + case AccountJsonFormat.GitHubExtended: { + const cloneEntity = Object.assign({}, this._originalEntity || {}); + delete (cloneEntity as any).cost; + delete (cloneEntity as any).headers; + return cloneEntity; + } case AccountJsonFormat.GitHubDetailedWithLink: { const cloneEntity = Object.assign({}, this._originalEntity || {}); delete (cloneEntity as any).cost; @@ -131,7 +137,7 @@ export class Account { return this._originalEntity ? this._originalEntity.name : undefined; } - constructor(entity, operations: IOperationsInstance, getAuthorizationHeader: IGetAuthorizationHeader) { + constructor(entity, operations: IOperationsInstance, getAuthorizationHeader: GetAuthorizationHeader) { common.assignKnownFieldsPrefixed( this, entity, @@ -144,7 +150,7 @@ export class Account { this._getAuthorizationHeader = getAuthorizationHeader; } - overrideAuthorization(getAuthorizationHeader: IGetAuthorizationHeader) { + overrideAuthorization(getAuthorizationHeader: GetAuthorizationHeader) { this._getAuthorizationHeader = getAuthorizationHeader; } @@ -352,8 +358,10 @@ export class Account { cacheOptions.backgroundRefresh = options.backgroundRefresh; } try { + const ops = operations as Operations; const entity = (await operations.github.request( - this.authorize(AppPurpose.Data), + ops.getPublicAuthorizationToken(), + // this.authorize(AppPurpose.Data), 'GET /user/:id', parameters, cacheOptions @@ -412,7 +420,7 @@ export class Account { }; const eventData = { github: { - id: id, + id, login: this._login, }, aad: aadIdentity, @@ -478,7 +486,11 @@ export class Account { return currentOrganizationMemberships; } - async removeCollaboratorPermissions(): Promise { + async removeCollaboratorPermissions( + onlyOneHundred?: boolean + ): Promise { + // NOTE: this at least temporarily adds the ability to punt 100 + // but not all grants; probably should use options eventually vs bool param. const history = []; const error: IReposError = null; const operations = throwIfNotGitHubCapable(this._operations); @@ -492,13 +504,18 @@ export class Account { await this.getDetails(); } const collaborativeRepos = await queryCache.userCollaboratorRepositories(this.id.toString()); + let i = 0; for (const entry of collaborativeRepos) { + if (onlyOneHundred && i >= 100) { + break; + } const { repository } = entry; try { await repository.getDetails(); if (repository.archived) { - history.push(`FYI: previous access to an archived repository ${repository.full_name}`); + history.push(`FYI: cannot alter prior grant to archived repository ${repository.full_name}`); } else { + ++i; await repository.removeCollaborator(this.login); history.push(`Removed ${this.login} as a Collaborator from the repository ${repository.full_name}`); } @@ -556,11 +573,8 @@ export class Account { return { history, error }; } - private authorize(purpose: AppPurpose): IGetAuthorizationHeader | string { - const getAuthorizationHeader = this._getAuthorizationHeader.bind( - this, - purpose - ) as IGetAuthorizationHeader; + private authorize(purpose: AppPurpose): GetAuthorizationHeader | string { + const getAuthorizationHeader = this._getAuthorizationHeader.bind(this, purpose) as GetAuthorizationHeader; return getAuthorizationHeader; } } diff --git a/business/application.ts b/business/application.ts index 40e0b602a..de134a1dc 100644 --- a/business/application.ts +++ b/business/application.ts @@ -3,14 +3,15 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +import { OrganizationSetting } from './entities/organizationSettings/organizationSetting'; import { IOperationsGitHubRestLibrary, IOperationsDefaultCacheTimes, - IGetAuthorizationHeader, + GetAuthorizationHeader, IGitHubAppInstallation, ICacheOptions, } from '../interfaces'; -import { wrapError } from '../utils'; +import { wrapError } from '../lib/utils'; const primaryInstallationProperties = [ 'id', @@ -22,13 +23,34 @@ const primaryInstallationProperties = [ 'events', ]; +export type GitHubAppDefinition = { + id: number; + slug: string; + friendlyName: string; +}; + +export function isInstallationConfigured( + settings: OrganizationSetting, + installation: IGitHubAppInstallation +): boolean { + if (!settings || !settings.installations) { + return false; + } + for (const install of settings.installations) { + if (install.installationId === installation.id) { + return true; + } + } + return false; +} + export default class GitHubApplication { constructor( private operations: IOperationsGitHubRestLibrary & IOperationsDefaultCacheTimes, public id: number, public slug: string, public friendlyName: string, - private getAuthorizationHeader: IGetAuthorizationHeader + private getAuthorizationHeader: GetAuthorizationHeader ) {} static PrimaryInstallationProperties = primaryInstallationProperties; @@ -52,6 +74,14 @@ export default class GitHubApplication { return invalid; } + asClientJson(): GitHubAppDefinition { + return { + id: this.id, + slug: this.slug, + friendlyName: this.friendlyName, + }; + } + async getInstallation(installationId: number, options?: ICacheOptions): Promise { const operations = this.operations; const parameters = { @@ -85,7 +115,7 @@ export default class GitHubApplication { async getInstallations(options?: ICacheOptions): Promise { options = options || {}; const operations = this.operations; - const getAuthorizationHeader = this.getAuthorizationHeader.bind(this) as IGetAuthorizationHeader; + const getAuthorizationHeader = this.getAuthorizationHeader.bind(this) as GetAuthorizationHeader; const github = operations.github; const caching = { maxAgeSeconds: options.maxAgeSeconds || operations.defaults.orgRepoDetailsStaleSeconds, // borrowing from another value @@ -102,8 +132,8 @@ export default class GitHubApplication { return installations; } - private authorize(): IGetAuthorizationHeader | string { - const getAuthorizationHeader = this.getAuthorizationHeader.bind(this) as IGetAuthorizationHeader; + private authorize(): GetAuthorizationHeader | string { + const getAuthorizationHeader = this.getAuthorizationHeader.bind(this) as GetAuthorizationHeader; return getAuthorizationHeader; } } diff --git a/business/collaborator.ts b/business/collaborator.ts index c40bc6f67..ed745527d 100644 --- a/business/collaborator.ts +++ b/business/collaborator.ts @@ -4,6 +4,7 @@ // import { GitHubRepositoryPermission, IGitHubCollaboratorPermissions } from '../interfaces'; +import { projectCollaboratorPermissionsObjectToGitHubRepositoryPermission } from '../lib/transitional'; import * as common from './common'; // prettier-ignore @@ -14,6 +15,19 @@ const memberPrimaryProperties = [ 'avatar_url', ]; +export type CollaboratorJson = { + avatar_url: string; + id: number; + login: string; + permissions: IGitHubCollaboratorPermissions; +}; + +export type CollaboratorAccount = Collaborator | { id: number; login: string }; + +export function compareCollaborators(a: Collaborator, b: Collaborator) { + return a?.login.localeCompare(b?.login, 'en', { sensitivity: 'base' }); +} + export class Collaborator { public static PrimaryProperties = memberPrimaryProperties; @@ -28,7 +42,7 @@ export class Collaborator { } } - asJson() { + asJson(): CollaboratorJson { return { avatar_url: this.avatar_url, id: this._id, @@ -45,15 +59,7 @@ export class Collaborator { if (!this._permissions) { return GitHubRepositoryPermission.None; } - const permissions = this._permissions; - if (permissions.admin) { - return GitHubRepositoryPermission.Admin; - } else if (permissions.push) { - return GitHubRepositoryPermission.Push; - } else if (permissions.pull) { - return GitHubRepositoryPermission.Pull; - } - throw new Error(`Unsupported permission type by getHighestPermission`); + return projectCollaboratorPermissionsObjectToGitHubRepositoryPermission(this._permissions); } get id(): number { diff --git a/business/domains.ts b/business/domains.ts index d283dac00..73d211f3a 100644 --- a/business/domains.ts +++ b/business/domains.ts @@ -3,13 +3,13 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { AppPurpose, AppPurposeTypes } from './githubApps'; +import { AppPurpose, AppPurposeTypes } from '../lib/github/appPurposes'; import { Organization } from '.'; import { IOperationsInstance, - IPurposefulGetAuthorizationHeader, + PurposefulGetAuthorizationHeader, throwIfNotGitHubCapable, - IGetAuthorizationHeader, + GetAuthorizationHeader, } from '../interfaces'; import { decorateIterable, @@ -17,7 +17,7 @@ import { IteratorResponse, PaginationPageSizeOptions, } from './iterable'; -import { DefaultGraphqlPageSize } from '../transitional'; +import { DefaultGraphqlPageSize } from '../lib/transitional'; type DomainResponse = { id: string; @@ -44,15 +44,15 @@ export class OrganizationDomains { private _organization: Organization; private _operations: IOperationsInstance; - private _getAuthorizationHeader: IPurposefulGetAuthorizationHeader; - private _getSpecificAuthorizationHeader: IPurposefulGetAuthorizationHeader; + private _getAuthorizationHeader: PurposefulGetAuthorizationHeader; + private _getSpecificAuthorizationHeader: PurposefulGetAuthorizationHeader; private _purpose: AppPurpose; constructor( organization: Organization, operations: IOperationsInstance, - getAuthorizationHeader: IPurposefulGetAuthorizationHeader, - getSpecificAuthorizationHeader: IPurposefulGetAuthorizationHeader + getAuthorizationHeader: PurposefulGetAuthorizationHeader, + getSpecificAuthorizationHeader: PurposefulGetAuthorizationHeader ) { this._getAuthorizationHeader = getAuthorizationHeader; this._getSpecificAuthorizationHeader = getSpecificAuthorizationHeader; @@ -110,19 +110,16 @@ export class OrganizationDomains { } } - private authorize(purpose: AppPurpose = this._purpose): IGetAuthorizationHeader { - const getAuthorizationHeader = this._getAuthorizationHeader.bind( - this, - purpose - ) as IGetAuthorizationHeader; + private authorize(purpose: AppPurpose = this._purpose): GetAuthorizationHeader { + const getAuthorizationHeader = this._getAuthorizationHeader.bind(this, purpose) as GetAuthorizationHeader; return getAuthorizationHeader; } - private authorizeSpecificPurpose(purpose: AppPurposeTypes): IGetAuthorizationHeader | string { + private authorizeSpecificPurpose(purpose: AppPurposeTypes): GetAuthorizationHeader | string { const getAuthorizationHeader = this._getSpecificAuthorizationHeader.bind( this, purpose - ) as IGetAuthorizationHeader; + ) as GetAuthorizationHeader; return getAuthorizationHeader; } } diff --git a/business/enterprise.ts b/business/enterprise.ts new file mode 100644 index 000000000..8c60aa5ae --- /dev/null +++ b/business/enterprise.ts @@ -0,0 +1,194 @@ +// +// Copyright (c) Microsoft. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +import { IProviders } from '../interfaces'; + +// TODO: paginate across enterprise fix + support iterators + +export type EnterpriseSamlExternalIdentityBasics = { + id: string; + user: { + login: string; + }; + samlIdentity: { + nameId: string; + }; +}; + +export type EnterpriseSamlExternalIdentityNode = { + node: EnterpriseSamlExternalIdentityBasics; +}; + +export default class GitHubEnterprise { + constructor( + private providers: IProviders, + public slug: string, + private administrativeToken: string + ) {} + + async getGitHubLoginForUserPrincipalName(userPrincipalName: string): Promise { + const node = await this.getSamlNodeFromUserPrincipalName(userPrincipalName); + return node?.user?.login; + } + + async getSamlNodeFromUserPrincipalName( + userPrincipalName: string + ): Promise { + const github = this.providers.github; + try { + const response = await github.graphql( + this.administrativeToken, + queries.getIdentityFromExternal, + { + enterpriseName: this.slug, + userPrincipalName, + }, + { + paginate: false, + } + ); + const nodes = response?.enterprise?.ownerInfo?.samlIdentityProvider?.externalIdentities + ?.edges as EnterpriseSamlExternalIdentityNode[]; + if (nodes.length > 0) { + return nodes[0].node; + } + } catch (error) { + throw error; + } + } + + async getSamlUserPrincipalNameForGitHubLogin(login: string): Promise { + const node = await this.getSamlNodeForGitHubLogin(login); + return node?.samlIdentity?.nameId; + } + + async getSamlNodeForGitHubLogin(login: string): Promise { + const github = this.providers.github; + try { + const response = await github.graphql( + this.administrativeToken, + queries.getIdentityFromGitHubLogin, + { + enterpriseName: this.slug, + login, + }, + { + paginate: false, + } + ); + const nodes = response?.enterprise?.ownerInfo?.samlIdentityProvider?.externalIdentities + ?.edges as EnterpriseSamlExternalIdentityNode[]; + if (nodes.length > 0) { + return nodes[0].node; + } + } catch (error) { + throw error; + } + } + + async getSamlMemberExternalIdentities(): Promise { + const fixedFirstFieldsCount = 8; + const github = this.providers.github; + try { + const response = await github.graphql( + this.administrativeToken, + queries.paginate, + { + enterpriseName: this.slug, + // id: this._id, + }, + { + paginate: false, // true, + } + ); + const nodes = response?.enterprise?.ownerInfo?.samlIdentityProvider?.externalIdentities + ?.edges as EnterpriseSamlExternalIdentityNode[]; + return nodes.map((node) => node.node); + } catch (error) { + throw error; + } + } +} + +const queries = { + getIdentityFromGitHubLogin: ` + query getIdentity($enterpriseName: String!, $login: String!) { + enterprise(slug: $enterpriseName) { + ownerInfo { + samlIdentityProvider { + externalIdentities(first: 5, login: $login) { + pageInfo { + hasNextPage + endCursor + } + edges { + node { + user { + login + } + samlIdentity { + nameId + } + } + } + } + } + } + } + } + `, + getIdentityFromExternal: ` + query getIdentity($enterpriseName: String!, $userPrincipalName: String!) { + enterprise(slug: $enterpriseName) { + ownerInfo { + samlIdentityProvider { + externalIdentities(first: 5, userName: $userPrincipalName) { + pageInfo { + hasNextPage + endCursor + } + edges { + node { + user { + login + } + samlIdentity { + nameId + } + } + } + } + } + } + } + } + `, + paginate: ` + query paginate($cursor: String, $enterpriseName: String!) { + enterprise(slug: $enterpriseName) { + ownerInfo { + samlIdentityProvider { + externalIdentities(after: $cursor, first: 100) { + pageInfo { + hasNextPage + endCursor + } + edges { + node { + user { + login + } + samlIdentity { + nameId + } + } + } + } + } + } + } + } + `, +}; diff --git a/entities/auditLogRecord/auditLogRecord.ts b/business/entities/auditLogRecord/auditLogRecord.ts similarity index 94% rename from entities/auditLogRecord/auditLogRecord.ts rename to business/entities/auditLogRecord/auditLogRecord.ts index a80b77e0a..08f2311ea 100644 --- a/entities/auditLogRecord/auditLogRecord.ts +++ b/business/entities/auditLogRecord/auditLogRecord.ts @@ -5,23 +5,23 @@ import { randomUUID } from 'crypto'; -import { EntityField } from '../../lib/entityMetadataProvider/entityMetadataProvider'; -import { IEntityMetadata } from '../../lib/entityMetadataProvider/entityMetadata'; -import { IEntityMetadataFixedQuery, FixedQueryType } from '../../lib/entityMetadataProvider/query'; +import { EntityField } from '../../../lib/entityMetadataProvider/entityMetadataProvider'; +import { IEntityMetadata } from '../../../lib/entityMetadataProvider/entityMetadata'; +import { IEntityMetadataFixedQuery, FixedQueryType } from '../../../lib/entityMetadataProvider/query'; import { EntityMetadataMappings, MetadataMappingDefinition, -} from '../../lib/entityMetadataProvider/declarations'; +} from '../../../lib/entityMetadataProvider/declarations'; import { Type } from './type'; import { PostgresJsonEntityQuery, PostgresSettings, PostgresConfiguration, -} from '../../lib/entityMetadataProvider/postgres'; -import { IDictionary } from '../../interfaces'; +} from '../../../lib/entityMetadataProvider/postgres'; +import { IDictionary } from '../../../interfaces'; import { AuditLogSource } from '.'; -import { stringOrNumberAsString } from '../../utils'; -import { MemoryConfiguration, MemorySettings } from '../../lib/entityMetadataProvider/memory'; +import { stringOrNumberAsString } from '../../../lib/utils'; +import { MemoryConfiguration, MemorySettings } from '../../../lib/entityMetadataProvider/memory'; const type = Type; diff --git a/entities/auditLogRecord/auditLogRecordProvider.ts b/business/entities/auditLogRecord/auditLogRecordProvider.ts similarity index 98% rename from entities/auditLogRecord/auditLogRecordProvider.ts rename to business/entities/auditLogRecord/auditLogRecordProvider.ts index df7b7647a..709df08c9 100644 --- a/entities/auditLogRecord/auditLogRecordProvider.ts +++ b/business/entities/auditLogRecord/auditLogRecordProvider.ts @@ -7,7 +7,7 @@ import { IEntityMetadata, EntityMetadataBase, IEntityMetadataBaseOptions, -} from '../../lib/entityMetadataProvider/entityMetadata'; +} from '../../../lib/entityMetadataProvider/entityMetadata'; import { AuditLogRecord, AuditLogRecordQueryUndoCandidatesByThirdPartyId, diff --git a/entities/auditLogRecord/index.ts b/business/entities/auditLogRecord/index.ts similarity index 100% rename from entities/auditLogRecord/index.ts rename to business/entities/auditLogRecord/index.ts diff --git a/entities/auditLogRecord/type.ts b/business/entities/auditLogRecord/type.ts similarity index 69% rename from entities/auditLogRecord/type.ts rename to business/entities/auditLogRecord/type.ts index de552af45..fb9ba2b8f 100644 --- a/entities/auditLogRecord/type.ts +++ b/business/entities/auditLogRecord/type.ts @@ -3,6 +3,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { EntityMetadataType } from '../../lib/entityMetadataProvider/entityMetadata'; +import { EntityMetadataType } from '../../../lib/entityMetadataProvider/entityMetadata'; export const Type = new EntityMetadataType('AuditLogRecord'); diff --git a/entities/localExtensionKey/index.ts b/business/entities/localExtensionKey/index.ts similarity index 91% rename from entities/localExtensionKey/index.ts rename to business/entities/localExtensionKey/index.ts index 0aff67b8e..015a8b11f 100644 --- a/entities/localExtensionKey/index.ts +++ b/business/entities/localExtensionKey/index.ts @@ -5,7 +5,7 @@ import { LocalExtensionKeyProvider } from './localExtensionKeyProvider'; import { LocalExtensionKey } from './localExtensionKey'; -import { IEntityMetadataProvider } from '../../lib/entityMetadataProvider/entityMetadataProvider'; +import { IEntityMetadataProvider } from '../../../lib/entityMetadataProvider/entityMetadataProvider'; export interface ILocalExtensionKeyProvider { initialize(): Promise; diff --git a/entities/localExtensionKey/localExtensionKey.ts b/business/entities/localExtensionKey/localExtensionKey.ts similarity index 90% rename from entities/localExtensionKey/localExtensionKey.ts rename to business/entities/localExtensionKey/localExtensionKey.ts index 0a0adb6ac..62dc3dd7c 100644 --- a/entities/localExtensionKey/localExtensionKey.ts +++ b/business/entities/localExtensionKey/localExtensionKey.ts @@ -5,15 +5,15 @@ import crypto from 'crypto'; -import { IObjectWithDefinedKeys } from '../../lib/entityMetadataProvider/entityMetadataProvider'; -import { EntityMetadataType, IEntityMetadata } from '../../lib/entityMetadataProvider/entityMetadata'; +import { IObjectWithDefinedKeys } from '../../../lib/entityMetadataProvider/entityMetadataProvider'; +import { EntityMetadataType, IEntityMetadata } from '../../../lib/entityMetadataProvider/entityMetadata'; import { MetadataMappingDefinition, EntityMetadataMappings, -} from '../../lib/entityMetadataProvider/declarations'; -import { IEntityMetadataFixedQuery, FixedQueryType } from '../../lib/entityMetadataProvider/query'; -import { TableSettings } from '../../lib/entityMetadataProvider/table'; -import { MemorySettings } from '../../lib/entityMetadataProvider/memory'; +} from '../../../lib/entityMetadataProvider/declarations'; +import { IEntityMetadataFixedQuery, FixedQueryType } from '../../../lib/entityMetadataProvider/query'; +import { TableSettings } from '../../../lib/entityMetadataProvider/table'; +import { MemorySettings } from '../../../lib/entityMetadataProvider/memory'; import { odata, TableEntityQueryOptions } from '@azure/data-tables'; const type = new EntityMetadataType('LocalExtensionKey'); diff --git a/entities/localExtensionKey/localExtensionKeyProvider.ts b/business/entities/localExtensionKey/localExtensionKeyProvider.ts similarity index 94% rename from entities/localExtensionKey/localExtensionKeyProvider.ts rename to business/entities/localExtensionKey/localExtensionKeyProvider.ts index 8b3c99097..adb37e2fd 100644 --- a/entities/localExtensionKey/localExtensionKeyProvider.ts +++ b/business/entities/localExtensionKey/localExtensionKeyProvider.ts @@ -3,10 +3,10 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { EntityMetadataBase } from '../../lib/entityMetadataProvider/entityMetadata'; +import { EntityMetadataBase } from '../../../lib/entityMetadataProvider/entityMetadata'; import { LocalExtensionKey, EntityImplementation } from './localExtensionKey'; import { ILocalExtensionKeyProvider, ILocalExtensionKeyProviderOptions } from '.'; -import { IEntityMetadataFixedQuery, FixedQueryType } from '../../lib/entityMetadataProvider/query'; +import { IEntityMetadataFixedQuery, FixedQueryType } from '../../../lib/entityMetadataProvider/query'; const thisProviderType = EntityImplementation.Type; diff --git a/entities/organizationAnnotation.ts b/business/entities/organizationAnnotation.ts similarity index 90% rename from entities/organizationAnnotation.ts rename to business/entities/organizationAnnotation.ts index 0fe6daf74..61b428b77 100644 --- a/entities/organizationAnnotation.ts +++ b/business/entities/organizationAnnotation.ts @@ -22,16 +22,16 @@ import { EntityMetadataType, IEntityMetadataBaseOptions, EntityMetadataBase, -} from '../lib/entityMetadataProvider/entityMetadata'; -import { IEntityMetadataFixedQuery, QueryBase } from '../lib/entityMetadataProvider/query'; +} from '../../lib/entityMetadataProvider/entityMetadata'; +import { IEntityMetadataFixedQuery, QueryBase } from '../../lib/entityMetadataProvider/query'; import { EntityMetadataMappings, MetadataMappingDefinition, -} from '../lib/entityMetadataProvider/declarations'; -import { PostgresSettings, PostgresConfiguration } from '../lib/entityMetadataProvider/postgres'; -import { IDictionary } from '../interfaces'; -import { CreateError, ErrorHelper } from '../transitional'; -import { MemoryConfiguration, TableConfiguration } from '../lib/entityMetadataProvider'; +} from '../../lib/entityMetadataProvider/declarations'; +import { PostgresSettings, PostgresConfiguration } from '../../lib/entityMetadataProvider/postgres'; +import { IDictionary } from '../../interfaces'; +import { CreateError, ErrorHelper } from '../../lib/transitional'; +import { MemoryConfiguration, TableConfiguration } from '../../lib/entityMetadataProvider'; const type = new EntityMetadataType('OrganizationAnnotation'); const thisProviderType = type; @@ -41,7 +41,10 @@ const defaultPostgresTableName = 'organizationannotations'; const organizationId = 'organizationId'; const primaryKeyFieldName = organizationId; -export enum OrganizationAnnotationProperty {} +export enum OrganizationAnnotationProperty { + Governance = 'governance', +} + export enum OrganizationAnnotationFeature {} export interface IOrganizationAnnotationChange { @@ -52,6 +55,11 @@ export interface IOrganizationAnnotationChange { text: string; } +export function getOrganizationAnnotationRestrictedPropertyNames(isSystemAdministrator?: boolean): string[] { + const restrictedProperties = ['administratorNodes', 'history']; + return isSystemAdministrator ? [] : restrictedProperties; +} + interface IOrganizationAnnotationMetadataProperties { // organizationId: string; // primary ID diff --git a/entities/organizationMemberCache/index.ts b/business/entities/organizationMemberCache/index.ts similarity index 100% rename from entities/organizationMemberCache/index.ts rename to business/entities/organizationMemberCache/index.ts diff --git a/entities/organizationMemberCache/organizationMemberCache.ts b/business/entities/organizationMemberCache/organizationMemberCache.ts similarity index 93% rename from entities/organizationMemberCache/organizationMemberCache.ts rename to business/entities/organizationMemberCache/organizationMemberCache.ts index 87a2e5b27..98d9506a5 100644 --- a/entities/organizationMemberCache/organizationMemberCache.ts +++ b/business/entities/organizationMemberCache/organizationMemberCache.ts @@ -3,25 +3,25 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { EntityField } from '../../lib/entityMetadataProvider/entityMetadataProvider'; -import { EntityMetadataType, IEntityMetadata } from '../../lib/entityMetadataProvider/entityMetadata'; -import { IEntityMetadataFixedQuery, FixedQueryType } from '../../lib/entityMetadataProvider/query'; +import { EntityField } from '../../../lib/entityMetadataProvider/entityMetadataProvider'; +import { EntityMetadataType, IEntityMetadata } from '../../../lib/entityMetadataProvider/entityMetadata'; +import { IEntityMetadataFixedQuery, FixedQueryType } from '../../../lib/entityMetadataProvider/query'; import { EntityMetadataMappings, MetadataMappingDefinition, -} from '../../lib/entityMetadataProvider/declarations'; -import { OrganizationMembershipRole } from '../../interfaces'; -import { stringOrNumberAsString } from '../../utils'; +} from '../../../lib/entityMetadataProvider/declarations'; +import { OrganizationMembershipRole } from '../../../interfaces'; +import { stringOrNumberAsString } from '../../../lib/utils'; import { PostgresJsonEntityQuery, PostgresGetAllEntities, PostgresSettings, PostgresConfiguration, -} from '../../lib/entityMetadataProvider/postgres'; -import { MemoryConfiguration, MemorySettings } from '../../lib/entityMetadataProvider/memory'; -import { TableConfiguration, TableSettings } from '../../lib/entityMetadataProvider'; +} from '../../../lib/entityMetadataProvider/postgres'; +import { MemoryConfiguration, MemorySettings } from '../../../lib/entityMetadataProvider/memory'; +import { TableConfiguration, TableSettings } from '../../../lib/entityMetadataProvider'; import { odata, TableEntityQueryOptions } from '@azure/data-tables'; -import { CreateError } from '../../transitional'; +import { CreateError } from '../../../lib/transitional'; const type = new EntityMetadataType('OrganizationMemberCache'); diff --git a/entities/organizationMemberCache/organizationMemberCacheProvider.ts b/business/entities/organizationMemberCache/organizationMemberCacheProvider.ts similarity index 98% rename from entities/organizationMemberCache/organizationMemberCacheProvider.ts rename to business/entities/organizationMemberCache/organizationMemberCacheProvider.ts index 66e789707..2aa1bdcb4 100644 --- a/entities/organizationMemberCache/organizationMemberCacheProvider.ts +++ b/business/entities/organizationMemberCache/organizationMemberCacheProvider.ts @@ -7,7 +7,7 @@ import { IEntityMetadata, EntityMetadataBase, IEntityMetadataBaseOptions, -} from '../../lib/entityMetadataProvider/entityMetadata'; +} from '../../../lib/entityMetadataProvider/entityMetadata'; import { OrganizationMemberCacheEntity, EntityImplementation, diff --git a/entities/organizationSettings/index.ts b/business/entities/organizationSettings/index.ts similarity index 100% rename from entities/organizationSettings/index.ts rename to business/entities/organizationSettings/index.ts diff --git a/entities/organizationSettings/organizationSetting.ts b/business/entities/organizationSettings/organizationSetting.ts similarity index 89% rename from entities/organizationSettings/organizationSetting.ts rename to business/entities/organizationSettings/organizationSetting.ts index 3b1705f60..5ce2cee28 100644 --- a/entities/organizationSettings/organizationSetting.ts +++ b/business/entities/organizationSettings/organizationSetting.ts @@ -3,15 +3,15 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { EntityField } from '../../lib/entityMetadataProvider/entityMetadataProvider'; -import { IEntityMetadata } from '../../lib/entityMetadataProvider/entityMetadata'; -import { IEntityMetadataFixedQuery, FixedQueryType } from '../../lib/entityMetadataProvider/query'; +import { EntityField } from '../../../lib/entityMetadataProvider/entityMetadataProvider'; +import { IEntityMetadata } from '../../../lib/entityMetadataProvider/entityMetadata'; +import { IEntityMetadataFixedQuery, FixedQueryType } from '../../../lib/entityMetadataProvider/query'; import { EntityMetadataMappings, MetadataMappingDefinition, -} from '../../lib/entityMetadataProvider/declarations'; +} from '../../../lib/entityMetadataProvider/declarations'; import { Type } from './type'; -import { PostgresGetAllEntities } from '../../lib/entityMetadataProvider/postgres'; +import { PostgresGetAllEntities } from '../../../lib/entityMetadataProvider/postgres'; import { MemoryConfiguration, MemorySettings, @@ -19,8 +19,8 @@ import { PostgresConfiguration, TableConfiguration, TableSettings, -} from '../../lib/entityMetadataProvider'; -import { ConfigGitHubOrganization } from '../../config/github.organizations.types'; +} from '../../../lib/entityMetadataProvider'; +import { ConfigGitHubOrganization } from '../../../config/github.organizations.types'; import { odata, TableEntityQueryOptions } from '@azure/data-tables'; export interface IBasicGitHubAppInstallation { @@ -29,8 +29,9 @@ export interface IBasicGitHubAppInstallation { appPurposeId?: string; } -export enum SpecialTeam { +export enum SystemTeam { Everyone = 'everyone', // teamAllMembers + OpenAccess = 'openAccess', Sudo = 'sudo', // teamSudoers GlobalSudo = 'globalSudo', // teamPortalSudoers SystemWrite = 'systemWrite', // teamAllReposWrite @@ -56,7 +57,7 @@ export enum OrganizationProperty { } export interface ISpecialTeam { - specialTeam: SpecialTeam; + specialTeam: SystemTeam; teamId: number; } @@ -158,6 +159,16 @@ export class OrganizationSetting implements IOrganizationSettingProperties { return this.#ownerToken; } + static CreateEmptyWithOldToken(token: string, notes: string, organizationId?: number) { + const settings = new OrganizationSetting(); + settings.#ownerToken = token; + if (organizationId) { + settings.organizationId = organizationId; + } + settings.operationsNotes = notes; + return settings; + } + static CreateFromStaticSettings(staticSettings: ConfigGitHubOrganization): OrganizationSetting { const clone = { ...staticSettings }; const settings = new OrganizationSetting(); @@ -260,20 +271,33 @@ export class OrganizationSetting implements IOrganizationSettingProperties { : [clone.teamAllMembers as any]; for (const value of arr) { settings.specialTeams.push({ - specialTeam: SpecialTeam.Everyone, + specialTeam: SystemTeam.Everyone, teamId: Number(value), }); } } delete clone.teamAllMembers; + if (clone.teamOpenAccess) { + const arr = Array.isArray(clone.teamOpenAccess) + ? (clone.teamOpenAccess as any[]) + : [clone.teamOpenAccess as any]; + for (const value of arr) { + settings.specialTeams.push({ + specialTeam: SystemTeam.OpenAccess, + teamId: Number(value), + }); + } + } + delete clone.teamOpenAccess; + if (clone.teamAllReposRead) { const arr = Array.isArray(clone.teamAllReposRead) ? (clone.teamAllReposRead as any[]) : [clone.teamAllReposRead as any]; for (const value of arr) { settings.specialTeams.push({ - specialTeam: SpecialTeam.SystemRead, + specialTeam: SystemTeam.SystemRead, teamId: Number(value), }); } @@ -286,7 +310,7 @@ export class OrganizationSetting implements IOrganizationSettingProperties { : [clone.teamAllReposWrite as any]; for (const value of arr) { settings.specialTeams.push({ - specialTeam: SpecialTeam.SystemWrite, + specialTeam: SystemTeam.SystemWrite, teamId: Number(value), }); } @@ -299,7 +323,7 @@ export class OrganizationSetting implements IOrganizationSettingProperties { : [clone.teamAllReposAdmin as any]; for (const value of arr) { settings.specialTeams.push({ - specialTeam: SpecialTeam.SystemAdmin, + specialTeam: SystemTeam.SystemAdmin, teamId: Number(value), }); } @@ -312,7 +336,7 @@ export class OrganizationSetting implements IOrganizationSettingProperties { : [clone.teamSudoers as any]; for (const value of arr) { settings.specialTeams.push({ - specialTeam: SpecialTeam.Sudo, + specialTeam: SystemTeam.Sudo, teamId: Number(value), }); } @@ -325,7 +349,7 @@ export class OrganizationSetting implements IOrganizationSettingProperties { : [clone.teamPortalSudoers as any]; for (const value of arr) { settings.specialTeams.push({ - specialTeam: SpecialTeam.GlobalSudo, + specialTeam: SystemTeam.GlobalSudo, teamId: Number(value), }); } diff --git a/entities/organizationSettings/organizationSettingProvider.ts b/business/entities/organizationSettings/organizationSettingProvider.ts similarity index 97% rename from entities/organizationSettings/organizationSettingProvider.ts rename to business/entities/organizationSettings/organizationSettingProvider.ts index 6cbf908cc..c7b7944dd 100644 --- a/entities/organizationSettings/organizationSettingProvider.ts +++ b/business/entities/organizationSettings/organizationSettingProvider.ts @@ -7,7 +7,7 @@ import { IEntityMetadata, EntityMetadataBase, IEntityMetadataBaseOptions, -} from '../../lib/entityMetadataProvider/entityMetadata'; +} from '../../../lib/entityMetadataProvider/entityMetadata'; import { OrganizationSetting, OrganizationSettingFixedQueryAll } from './organizationSetting'; import { EntityImplementation } from './organizationSetting'; diff --git a/entities/organizationSettings/type.ts b/business/entities/organizationSettings/type.ts similarity index 69% rename from entities/organizationSettings/type.ts rename to business/entities/organizationSettings/type.ts index 6f3cd7419..0d4bc1a6c 100644 --- a/entities/organizationSettings/type.ts +++ b/business/entities/organizationSettings/type.ts @@ -3,6 +3,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { EntityMetadataType } from '../../lib/entityMetadataProvider/entityMetadata'; +import { EntityMetadataType } from '../../../lib/entityMetadataProvider/entityMetadata'; export const Type = new EntityMetadataType('OrganizationSetting'); diff --git a/entities/repository.ts b/business/entities/repository.ts similarity index 92% rename from entities/repository.ts rename to business/entities/repository.ts index c2ca9ce3c..bb1b5bd5a 100644 --- a/entities/repository.ts +++ b/business/entities/repository.ts @@ -3,7 +3,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { GitHubRepositoryVisibility } from '../interfaces'; +import { GitHubRepositoryVisibility } from '../../interfaces'; import { EntityMetadataBase, EntityMetadataMappings, @@ -14,8 +14,9 @@ import { keyValueMetadataField, MetadataMappingDefinition, QueryBase, -} from '../lib/entityMetadataProvider'; -import { PostgresConfiguration, PostgresSettings } from '../lib/entityMetadataProvider/postgres'; +} from '../../lib/entityMetadataProvider'; +import { PostgresConfiguration, PostgresSettings } from '../../lib/entityMetadataProvider/postgres'; +import { ErrorHelper } from '../../lib/transitional'; const type = new EntityMetadataType('RepositoryDetails'); const typeColumnValue = 'repositorydetails'; @@ -34,7 +35,10 @@ class ThisQueryBase extends QueryBase { } class ThisQuery extends ThisQueryBase { - constructor(query: Query, public parameters: T) { + constructor( + query: Query, + public parameters: T + ) { super(query); if (!this.parameters) { this.parameters = {} as T; @@ -312,6 +316,21 @@ for (let i = 0; i < fieldNames.length; i++) { } } +export async function tryGetRepositoryEntity( + repositoryProvider: IRepositoryProvider, + repositoryId: number +): Promise { + try { + const repositoryEntity = await repositoryProvider.get(repositoryId); + return repositoryEntity; + } catch (error) { + if (ErrorHelper.IsNotFound(error)) { + return null; + } + throw error; + } +} + export const EntityImplementation = { Type: type, EnsureDefinitions: () => {}, diff --git a/entities/repositoryCache/index.ts b/business/entities/repositoryCache/index.ts similarity index 100% rename from entities/repositoryCache/index.ts rename to business/entities/repositoryCache/index.ts diff --git a/entities/repositoryCache/repositoryCache.ts b/business/entities/repositoryCache/repositoryCache.ts similarity index 91% rename from entities/repositoryCache/repositoryCache.ts rename to business/entities/repositoryCache/repositoryCache.ts index 78da4fe3d..f88af3a4b 100644 --- a/entities/repositoryCache/repositoryCache.ts +++ b/business/entities/repositoryCache/repositoryCache.ts @@ -3,23 +3,23 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { EntityField } from '../../lib/entityMetadataProvider/entityMetadataProvider'; -import { EntityMetadataType, IEntityMetadata } from '../../lib/entityMetadataProvider/entityMetadata'; -import { IEntityMetadataFixedQuery, FixedQueryType } from '../../lib/entityMetadataProvider/query'; +import { EntityField } from '../../../lib/entityMetadataProvider/entityMetadataProvider'; +import { EntityMetadataType, IEntityMetadata } from '../../../lib/entityMetadataProvider/entityMetadata'; +import { IEntityMetadataFixedQuery, FixedQueryType } from '../../../lib/entityMetadataProvider/query'; import { EntityMetadataMappings, MetadataMappingDefinition, -} from '../../lib/entityMetadataProvider/declarations'; +} from '../../../lib/entityMetadataProvider/declarations'; import { PostgresGetAllEntities, PostgresJsonEntityQuery, PostgresSettings, PostgresConfiguration, -} from '../../lib/entityMetadataProvider/postgres'; -import { stringOrNumberAsString } from '../../utils'; -import { MemoryConfiguration, MemorySettings } from '../../lib/entityMetadataProvider/memory'; -import { Operations } from '../../business/operations'; -import { TableConfiguration } from '../../lib/entityMetadataProvider'; +} from '../../../lib/entityMetadataProvider/postgres'; +import { stringOrNumberAsString } from '../../../lib/utils'; +import { MemoryConfiguration, MemorySettings } from '../../../lib/entityMetadataProvider/memory'; +import { Operations } from '../../operations'; +import { TableConfiguration } from '../../../lib/entityMetadataProvider'; const type = new EntityMetadataType('RepositoryCache'); diff --git a/entities/repositoryCache/repositoryCacheProvider.ts b/business/entities/repositoryCache/repositoryCacheProvider.ts similarity index 98% rename from entities/repositoryCache/repositoryCacheProvider.ts rename to business/entities/repositoryCache/repositoryCacheProvider.ts index 9109917d2..08b868c62 100644 --- a/entities/repositoryCache/repositoryCacheProvider.ts +++ b/business/entities/repositoryCache/repositoryCacheProvider.ts @@ -7,7 +7,7 @@ import { IEntityMetadata, EntityMetadataBase, IEntityMetadataBaseOptions, -} from '../../lib/entityMetadataProvider/entityMetadata'; +} from '../../../lib/entityMetadataProvider/entityMetadata'; import { RepositoryCacheEntity, EntityImplementation, diff --git a/entities/repositoryCollaboratorCache/index.ts b/business/entities/repositoryCollaboratorCache/index.ts similarity index 100% rename from entities/repositoryCollaboratorCache/index.ts rename to business/entities/repositoryCollaboratorCache/index.ts diff --git a/entities/repositoryCollaboratorCache/repositoryCollaboratorCache.ts b/business/entities/repositoryCollaboratorCache/repositoryCollaboratorCache.ts similarity index 94% rename from entities/repositoryCollaboratorCache/repositoryCollaboratorCache.ts rename to business/entities/repositoryCollaboratorCache/repositoryCollaboratorCache.ts index 72e3c7a53..c701be627 100644 --- a/entities/repositoryCollaboratorCache/repositoryCollaboratorCache.ts +++ b/business/entities/repositoryCollaboratorCache/repositoryCollaboratorCache.ts @@ -3,23 +3,23 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { EntityField } from '../../lib/entityMetadataProvider/entityMetadataProvider'; -import { EntityMetadataType, IEntityMetadata } from '../../lib/entityMetadataProvider/entityMetadata'; -import { IEntityMetadataFixedQuery, FixedQueryType } from '../../lib/entityMetadataProvider/query'; +import { EntityField } from '../../../lib/entityMetadataProvider/entityMetadataProvider'; +import { EntityMetadataType, IEntityMetadata } from '../../../lib/entityMetadataProvider/entityMetadata'; +import { IEntityMetadataFixedQuery, FixedQueryType } from '../../../lib/entityMetadataProvider/query'; import { EntityMetadataMappings, MetadataMappingDefinition, -} from '../../lib/entityMetadataProvider/declarations'; -import { GitHubCollaboratorType, GitHubRepositoryPermission } from '../../interfaces'; +} from '../../../lib/entityMetadataProvider/declarations'; +import { GitHubCollaboratorType, GitHubRepositoryPermission } from '../../../interfaces'; import { PostgresGetAllEntities, PostgresJsonEntityQuery, PostgresSettings, PostgresConfiguration, -} from '../../lib/entityMetadataProvider/postgres'; -import { stringOrNumberAsString } from '../../utils'; -import { MemoryConfiguration, MemorySettings } from '../../lib/entityMetadataProvider/memory'; -import { TableConfiguration } from '../../lib/entityMetadataProvider'; +} from '../../../lib/entityMetadataProvider/postgres'; +import { stringOrNumberAsString } from '../../../lib/utils'; +import { MemoryConfiguration, MemorySettings } from '../../../lib/entityMetadataProvider/memory'; +import { TableConfiguration } from '../../../lib/entityMetadataProvider'; const type = new EntityMetadataType('RepositoryCollaboratorCache'); diff --git a/entities/repositoryCollaboratorCache/repositoryCollaboratorCacheProvider.ts b/business/entities/repositoryCollaboratorCache/repositoryCollaboratorCacheProvider.ts similarity index 99% rename from entities/repositoryCollaboratorCache/repositoryCollaboratorCacheProvider.ts rename to business/entities/repositoryCollaboratorCache/repositoryCollaboratorCacheProvider.ts index b7da8d4d3..93dd18710 100644 --- a/entities/repositoryCollaboratorCache/repositoryCollaboratorCacheProvider.ts +++ b/business/entities/repositoryCollaboratorCache/repositoryCollaboratorCacheProvider.ts @@ -7,7 +7,7 @@ import { IEntityMetadata, EntityMetadataBase, IEntityMetadataBaseOptions, -} from '../../lib/entityMetadataProvider/entityMetadata'; +} from '../../../lib/entityMetadataProvider/entityMetadata'; import { RepositoryCollaboratorCacheEntity, EntityImplementation, diff --git a/entities/repositoryMetadata/index.ts b/business/entities/repositoryMetadata/index.ts similarity index 100% rename from entities/repositoryMetadata/index.ts rename to business/entities/repositoryMetadata/index.ts diff --git a/entities/repositoryMetadata/repositoryMetadata.ts b/business/entities/repositoryMetadata/repositoryMetadata.ts similarity index 95% rename from entities/repositoryMetadata/repositoryMetadata.ts rename to business/entities/repositoryMetadata/repositoryMetadata.ts index c762557aa..015290fb0 100644 --- a/entities/repositoryMetadata/repositoryMetadata.ts +++ b/business/entities/repositoryMetadata/repositoryMetadata.ts @@ -3,28 +3,28 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { EntityField } from '../../lib/entityMetadataProvider/entityMetadataProvider'; -import { IEntityMetadata } from '../../lib/entityMetadataProvider/entityMetadata'; -import { IEntityMetadataFixedQuery, FixedQueryType } from '../../lib/entityMetadataProvider/query'; +import { EntityField } from '../../../lib/entityMetadataProvider/entityMetadataProvider'; +import type { IEntityMetadata } from '../../../lib/entityMetadataProvider/entityMetadata'; +import { type IEntityMetadataFixedQuery, FixedQueryType } from '../../../lib/entityMetadataProvider/query'; import { EntityMetadataMappings, MetadataMappingDefinition, -} from '../../lib/entityMetadataProvider/declarations'; +} from '../../../lib/entityMetadataProvider/declarations'; import { Type } from './type'; import { PostgresGetAllEntities, PostgresGetByID, PostgresSettings, PostgresConfiguration, -} from '../../lib/entityMetadataProvider/postgres'; -import { TableConfiguration, TableSettings } from '../../lib/entityMetadataProvider/table'; -import { MemoryConfiguration, MemorySettings } from '../../lib/entityMetadataProvider/memory'; +} from '../../../lib/entityMetadataProvider/postgres'; +import { TableSettings } from '../../../lib/entityMetadataProvider/table'; +import { MemoryConfiguration, MemorySettings } from '../../../lib/entityMetadataProvider/memory'; import { odata, TableEntityQueryOptions } from '@azure/data-tables'; import { GitHubRepositoryVisibility, IInitialTeamPermission, RepositoryLockdownState, -} from '../../interfaces/github/repos'; +} from '../../../interfaces/github/repos'; const type = Type; diff --git a/entities/repositoryMetadata/repositoryMetadataProvider.ts b/business/entities/repositoryMetadata/repositoryMetadataProvider.ts similarity index 98% rename from entities/repositoryMetadata/repositoryMetadataProvider.ts rename to business/entities/repositoryMetadata/repositoryMetadataProvider.ts index 9cd0d39b2..754486fb6 100644 --- a/entities/repositoryMetadata/repositoryMetadataProvider.ts +++ b/business/entities/repositoryMetadata/repositoryMetadataProvider.ts @@ -7,7 +7,7 @@ import { IEntityMetadata, EntityMetadataBase, IEntityMetadataBaseOptions, -} from '../../lib/entityMetadataProvider/entityMetadata'; +} from '../../../lib/entityMetadataProvider/entityMetadata'; import { RepositoryMetadataEntity, RepositoryMetadataFixedQueryAll, diff --git a/entities/repositoryMetadata/type.ts b/business/entities/repositoryMetadata/type.ts similarity index 68% rename from entities/repositoryMetadata/type.ts rename to business/entities/repositoryMetadata/type.ts index d86789e50..3c560ca42 100644 --- a/entities/repositoryMetadata/type.ts +++ b/business/entities/repositoryMetadata/type.ts @@ -3,6 +3,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { EntityMetadataType } from '../../lib/entityMetadataProvider/entityMetadata'; +import { EntityMetadataType } from '../../../lib/entityMetadataProvider/entityMetadata'; export const Type = new EntityMetadataType('Repository'); diff --git a/entities/repositoryTeamCache/index.ts b/business/entities/repositoryTeamCache/index.ts similarity index 100% rename from entities/repositoryTeamCache/index.ts rename to business/entities/repositoryTeamCache/index.ts diff --git a/entities/repositoryTeamCache/repositoryTeamCache.ts b/business/entities/repositoryTeamCache/repositoryTeamCache.ts similarity index 93% rename from entities/repositoryTeamCache/repositoryTeamCache.ts rename to business/entities/repositoryTeamCache/repositoryTeamCache.ts index 2d4147517..c6a0cf66e 100644 --- a/entities/repositoryTeamCache/repositoryTeamCache.ts +++ b/business/entities/repositoryTeamCache/repositoryTeamCache.ts @@ -3,24 +3,24 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { EntityField } from '../../lib/entityMetadataProvider/entityMetadataProvider'; -import { EntityMetadataType, IEntityMetadata } from '../../lib/entityMetadataProvider/entityMetadata'; -import { IEntityMetadataFixedQuery, FixedQueryType } from '../../lib/entityMetadataProvider/query'; +import { EntityField } from '../../../lib/entityMetadataProvider/entityMetadataProvider'; +import { EntityMetadataType, IEntityMetadata } from '../../../lib/entityMetadataProvider/entityMetadata'; +import { IEntityMetadataFixedQuery, FixedQueryType } from '../../../lib/entityMetadataProvider/query'; import { EntityMetadataMappings, MetadataMappingDefinition, -} from '../../lib/entityMetadataProvider/declarations'; +} from '../../../lib/entityMetadataProvider/declarations'; import { PostgresGetAllEntities, PostgresJsonEntityQuery, PostgresJsonEntityQueryMultiple, PostgresSettings, PostgresConfiguration, -} from '../../lib/entityMetadataProvider/postgres'; -import { stringOrNumberAsString } from '../../utils'; -import { GitHubRepositoryPermission } from '../../interfaces/github/repos'; -import { MemoryConfiguration, MemorySettings } from '../../lib/entityMetadataProvider/memory'; -import { TableConfiguration } from '../../lib/entityMetadataProvider'; +} from '../../../lib/entityMetadataProvider/postgres'; +import { stringOrNumberAsString } from '../../../lib/utils'; +import { GitHubRepositoryPermission } from '../../../interfaces/github/repos'; +import { MemoryConfiguration, MemorySettings } from '../../../lib/entityMetadataProvider/memory'; +import { TableConfiguration } from '../../../lib/entityMetadataProvider'; const type = new EntityMetadataType('RepositoryTeamCache'); diff --git a/entities/repositoryTeamCache/repositoryTeamCacheProvider.ts b/business/entities/repositoryTeamCache/repositoryTeamCacheProvider.ts similarity index 98% rename from entities/repositoryTeamCache/repositoryTeamCacheProvider.ts rename to business/entities/repositoryTeamCache/repositoryTeamCacheProvider.ts index 72fb0a38a..f22fd79d4 100644 --- a/entities/repositoryTeamCache/repositoryTeamCacheProvider.ts +++ b/business/entities/repositoryTeamCache/repositoryTeamCacheProvider.ts @@ -7,7 +7,7 @@ import { IEntityMetadata, EntityMetadataBase, IEntityMetadataBaseOptions, -} from '../../lib/entityMetadataProvider/entityMetadata'; +} from '../../../lib/entityMetadataProvider/entityMetadata'; import { RepositoryTeamCacheEntity, EntityImplementation, diff --git a/entities/teamCache/index.ts b/business/entities/teamCache/index.ts similarity index 97% rename from entities/teamCache/index.ts rename to business/entities/teamCache/index.ts index 57d9f81a8..aef008643 100644 --- a/entities/teamCache/index.ts +++ b/business/entities/teamCache/index.ts @@ -4,7 +4,7 @@ // import { ITeamCacheProvider, ITeamCacheCreateOptions, TeamCacheProvider } from './teamCacheProvider'; -import { FixedQueryType, IEntityMetadataFixedQuery } from '../../lib/entityMetadataProvider/query'; +import { FixedQueryType, IEntityMetadataFixedQuery } from '../../../lib/entityMetadataProvider/query'; export async function CreateTeamCacheProviderInstance( options?: ITeamCacheCreateOptions diff --git a/entities/teamCache/teamCache.ts b/business/entities/teamCache/teamCache.ts similarity index 89% rename from entities/teamCache/teamCache.ts rename to business/entities/teamCache/teamCache.ts index f5f3f3ffb..99bb3592c 100644 --- a/entities/teamCache/teamCache.ts +++ b/business/entities/teamCache/teamCache.ts @@ -3,23 +3,23 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { EntityField } from '../../lib/entityMetadataProvider/entityMetadataProvider'; -import { EntityMetadataType, IEntityMetadata } from '../../lib/entityMetadataProvider/entityMetadata'; -import { IEntityMetadataFixedQuery, FixedQueryType } from '../../lib/entityMetadataProvider/query'; +import { EntityField } from '../../../lib/entityMetadataProvider/entityMetadataProvider'; +import { EntityMetadataType, IEntityMetadata } from '../../../lib/entityMetadataProvider/entityMetadata'; +import { IEntityMetadataFixedQuery, FixedQueryType } from '../../../lib/entityMetadataProvider/query'; import { EntityMetadataMappings, MetadataMappingDefinition, -} from '../../lib/entityMetadataProvider/declarations'; +} from '../../../lib/entityMetadataProvider/declarations'; import { PostgresJsonEntityQuery, PostgresGetAllEntities, PostgresSettings, PostgresConfiguration, -} from '../../lib/entityMetadataProvider/postgres'; +} from '../../../lib/entityMetadataProvider/postgres'; import { TeamCacheFixedQueryByOrganizationId, TeamCacheDeleteByOrganizationId } from '.'; -import { stringOrNumberAsString } from '../../utils'; -import { MemoryConfiguration, MemorySettings } from '../../lib/entityMetadataProvider/memory'; -import { TableConfiguration } from '../../lib/entityMetadataProvider'; +import { stringOrNumberAsString } from '../../../lib/utils'; +import { MemoryConfiguration, MemorySettings } from '../../../lib/entityMetadataProvider/memory'; +import { TableConfiguration } from '../../../lib/entityMetadataProvider'; const type = new EntityMetadataType('TeamCache'); diff --git a/entities/teamCache/teamCacheProvider.ts b/business/entities/teamCache/teamCacheProvider.ts similarity index 98% rename from entities/teamCache/teamCacheProvider.ts rename to business/entities/teamCache/teamCacheProvider.ts index dc29686b8..326aca957 100644 --- a/entities/teamCache/teamCacheProvider.ts +++ b/business/entities/teamCache/teamCacheProvider.ts @@ -7,7 +7,7 @@ import { IEntityMetadata, EntityMetadataBase, IEntityMetadataBaseOptions, -} from '../../lib/entityMetadataProvider/entityMetadata'; +} from '../../../lib/entityMetadataProvider/entityMetadata'; import { TeamCacheEntity } from './teamCache'; import { TeamCacheFixedQueryAll, diff --git a/entities/teamJoinApproval/approvalProvider.ts b/business/entities/teamJoinApproval/approvalProvider.ts similarity index 90% rename from entities/teamJoinApproval/approvalProvider.ts rename to business/entities/teamJoinApproval/approvalProvider.ts index 95a9acaad..7e3988fc6 100644 --- a/entities/teamJoinApproval/approvalProvider.ts +++ b/business/entities/teamJoinApproval/approvalProvider.ts @@ -4,7 +4,7 @@ // import { TeamJoinApprovalEntity } from './teamJoinApproval'; -import { IEntityMetadataProvider } from '../../lib/entityMetadataProvider/entityMetadataProvider'; +import { IEntityMetadataProvider } from '../../../lib/entityMetadataProvider/entityMetadataProvider'; export interface IApprovalProvider { initialize(): Promise; diff --git a/entities/teamJoinApproval/index.ts b/business/entities/teamJoinApproval/index.ts similarity index 63% rename from entities/teamJoinApproval/index.ts rename to business/entities/teamJoinApproval/index.ts index a7936f8bd..ec378ba3c 100644 --- a/entities/teamJoinApproval/index.ts +++ b/business/entities/teamJoinApproval/index.ts @@ -3,11 +3,8 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { - IApprovalProvider, - IApprovalProviderCreateOptions, -} from '../../entities/teamJoinApproval/approvalProvider'; -import { TeamJoinApprovalProvider } from '../../entities/teamJoinApproval/teamJoinApprovalProvider'; +import { IApprovalProvider, IApprovalProviderCreateOptions } from './approvalProvider'; +import { TeamJoinApprovalProvider } from './teamJoinApprovalProvider'; export async function createAndInitializeApprovalProviderInstance( options: IApprovalProviderCreateOptions diff --git a/entities/teamJoinApproval/teamJoinApproval.ts b/business/entities/teamJoinApproval/teamJoinApproval.ts similarity index 96% rename from entities/teamJoinApproval/teamJoinApproval.ts rename to business/entities/teamJoinApproval/teamJoinApproval.ts index e9a049b2b..443d39372 100644 --- a/entities/teamJoinApproval/teamJoinApproval.ts +++ b/business/entities/teamJoinApproval/teamJoinApproval.ts @@ -5,22 +5,22 @@ import { randomUUID } from 'crypto'; -import { IObjectWithDefinedKeys } from '../../lib/entityMetadataProvider/entityMetadataProvider'; -import { EntityMetadataType, IEntityMetadata } from '../../lib/entityMetadataProvider/entityMetadata'; +import { IObjectWithDefinedKeys } from '../../../lib/entityMetadataProvider/entityMetadataProvider'; +import { EntityMetadataType, IEntityMetadata } from '../../../lib/entityMetadataProvider/entityMetadata'; import { MetadataMappingDefinition, EntityMetadataMappings, -} from '../../lib/entityMetadataProvider/declarations'; -import { FixedQueryType, IEntityMetadataFixedQuery } from '../../lib/entityMetadataProvider/query'; -import { stringOrNumberAsString, stringOrNumberArrayAsStringArray } from '../../utils'; +} from '../../../lib/entityMetadataProvider/declarations'; +import { FixedQueryType, IEntityMetadataFixedQuery } from '../../../lib/entityMetadataProvider/query'; +import { stringOrNumberAsString, stringOrNumberArrayAsStringArray } from '../../../lib/utils'; import { PostgresGetAllEntities, PostgresJsonEntityQuery, PostgresSettings, PostgresConfiguration, -} from '../../lib/entityMetadataProvider/postgres'; -import { TableSettings } from '../../lib/entityMetadataProvider/table'; -import { MemorySettings } from '../../lib/entityMetadataProvider/memory'; +} from '../../../lib/entityMetadataProvider/postgres'; +import { TableSettings } from '../../../lib/entityMetadataProvider/table'; +import { MemorySettings } from '../../../lib/entityMetadataProvider/memory'; import { odata, TableEntityQueryOptions } from '@azure/data-tables'; const type = new EntityMetadataType('TeamJoinRequest'); diff --git a/entities/teamJoinApproval/teamJoinApprovalProvider.ts b/business/entities/teamJoinApproval/teamJoinApprovalProvider.ts similarity index 98% rename from entities/teamJoinApproval/teamJoinApprovalProvider.ts rename to business/entities/teamJoinApproval/teamJoinApprovalProvider.ts index 2cf004b0f..1a5bb4691 100644 --- a/entities/teamJoinApproval/teamJoinApprovalProvider.ts +++ b/business/entities/teamJoinApproval/teamJoinApprovalProvider.ts @@ -7,7 +7,7 @@ import { IApprovalProvider } from './approvalProvider'; import { IEntityMetadataBaseOptions, EntityMetadataBase, -} from '../../lib/entityMetadataProvider/entityMetadata'; +} from '../../../lib/entityMetadataProvider/entityMetadata'; import { TeamJoinApprovalEntity, TeamJoinRequestFixedQueryByTeam, diff --git a/entities/teamMemberCache/index.ts b/business/entities/teamMemberCache/index.ts similarity index 95% rename from entities/teamMemberCache/index.ts rename to business/entities/teamMemberCache/index.ts index 652f1b78a..d799cc026 100644 --- a/entities/teamMemberCache/index.ts +++ b/business/entities/teamMemberCache/index.ts @@ -8,7 +8,7 @@ import { ITeamMemberCacheCreateOptions, TeamMemberCacheProvider, } from './teamMemberCacheProvider'; -import { FixedQueryType, IEntityMetadataFixedQuery } from '../../lib/entityMetadataProvider/query'; +import { FixedQueryType, IEntityMetadataFixedQuery } from '../../../lib/entityMetadataProvider/query'; export async function CreateTeamMemberCacheProviderInstance( options?: ITeamMemberCacheCreateOptions @@ -64,7 +64,10 @@ export class TeamMemberCacheFixedQueryByUserId implements IEntityMetadataFixedQu export class TeamMemberCacheFixedQueryByOrganizationIdAndUserId implements IEntityMetadataFixedQuery { public readonly fixedQueryType: FixedQueryType = FixedQueryType.TeamMemberCacheGetByOrganizationIdAndUserId; - constructor(public organizationId: string, public userId: string) { + constructor( + public organizationId: string, + public userId: string + ) { if (typeof this.userId !== 'string') { throw new Error(`userId ${userId} must be a string`); } diff --git a/entities/teamMemberCache/teamMemberCache.ts b/business/entities/teamMemberCache/teamMemberCache.ts similarity index 91% rename from entities/teamMemberCache/teamMemberCache.ts rename to business/entities/teamMemberCache/teamMemberCache.ts index 2cffaaa20..3a7c107a6 100644 --- a/entities/teamMemberCache/teamMemberCache.ts +++ b/business/entities/teamMemberCache/teamMemberCache.ts @@ -3,13 +3,13 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { EntityField } from '../../lib/entityMetadataProvider/entityMetadataProvider'; -import { EntityMetadataType, IEntityMetadata } from '../../lib/entityMetadataProvider/entityMetadata'; -import { IEntityMetadataFixedQuery, FixedQueryType } from '../../lib/entityMetadataProvider/query'; +import { EntityField } from '../../../lib/entityMetadataProvider/entityMetadataProvider'; +import { EntityMetadataType, IEntityMetadata } from '../../../lib/entityMetadataProvider/entityMetadata'; +import { IEntityMetadataFixedQuery, FixedQueryType } from '../../../lib/entityMetadataProvider/query'; import { EntityMetadataMappings, MetadataMappingDefinition, -} from '../../lib/entityMetadataProvider/declarations'; +} from '../../../lib/entityMetadataProvider/declarations'; import { TeamMemberCacheFixedQueryByOrganizationId, TeamMemberCacheFixedQueryByUserId, @@ -17,16 +17,16 @@ import { TeamMemberCacheFixedQueryByOrganizationIdAndUserId, TeamMemberCacheDeleteByOrganizationId, } from '.'; -import { GitHubTeamRole } from '../../interfaces'; +import { GitHubTeamRole } from '../../../interfaces'; import { PostgresGetAllEntities, PostgresJsonEntityQuery, PostgresSettings, PostgresConfiguration, -} from '../../lib/entityMetadataProvider/postgres'; -import { stringOrNumberAsString } from '../../utils'; -import { MemoryConfiguration, MemorySettings } from '../../lib/entityMetadataProvider/memory'; -import { TableConfiguration } from '../../lib/entityMetadataProvider'; +} from '../../../lib/entityMetadataProvider/postgres'; +import { stringOrNumberAsString } from '../../../lib/utils'; +import { MemoryConfiguration, MemorySettings } from '../../../lib/entityMetadataProvider/memory'; +import { TableConfiguration } from '../../../lib/entityMetadataProvider'; const type = new EntityMetadataType('TeamMemberCache'); diff --git a/entities/teamMemberCache/teamMemberCacheProvider.ts b/business/entities/teamMemberCache/teamMemberCacheProvider.ts similarity index 99% rename from entities/teamMemberCache/teamMemberCacheProvider.ts rename to business/entities/teamMemberCache/teamMemberCacheProvider.ts index c9083fe39..dc109276a 100644 --- a/entities/teamMemberCache/teamMemberCacheProvider.ts +++ b/business/entities/teamMemberCache/teamMemberCacheProvider.ts @@ -7,7 +7,7 @@ import { IEntityMetadata, EntityMetadataBase, IEntityMetadataBaseOptions, -} from '../../lib/entityMetadataProvider/entityMetadata'; +} from '../../../lib/entityMetadataProvider/entityMetadata'; import { TeamMemberCacheEntity, EntityImplementation } from './teamMemberCache'; import { TeamMemberCacheFixedQueryAll, diff --git a/entities/token/index.ts b/business/entities/token/index.ts similarity index 90% rename from entities/token/index.ts rename to business/entities/token/index.ts index 98f14a4b0..5d152e2b7 100644 --- a/entities/token/index.ts +++ b/business/entities/token/index.ts @@ -5,7 +5,7 @@ import { TokenProvider } from './tokenProvider'; import { PersonalAccessToken } from './token'; -import { IEntityMetadataBaseOptions } from '../../lib/entityMetadataProvider/entityMetadata'; +import { IEntityMetadataBaseOptions } from '../../../lib/entityMetadataProvider/entityMetadata'; export interface ITokenProvider { initialize(): Promise; diff --git a/entities/token/token.ts b/business/entities/token/token.ts similarity index 92% rename from entities/token/token.ts rename to business/entities/token/token.ts index 0c427cee0..518dbe6db 100644 --- a/entities/token/token.ts +++ b/business/entities/token/token.ts @@ -5,25 +5,28 @@ import crypto from 'crypto'; -import { EntityField, IObjectWithDefinedKeys } from '../../lib/entityMetadataProvider/entityMetadataProvider'; -import { EntityMetadataType, IEntityMetadata } from '../../lib/entityMetadataProvider/entityMetadata'; +import { + EntityField, + IObjectWithDefinedKeys, +} from '../../../lib/entityMetadataProvider/entityMetadataProvider'; +import { EntityMetadataType, IEntityMetadata } from '../../../lib/entityMetadataProvider/entityMetadata'; import { MetadataMappingDefinition, EntityMetadataMappings, -} from '../../lib/entityMetadataProvider/declarations'; -import { IEntityMetadataFixedQuery, FixedQueryType } from '../../lib/entityMetadataProvider/query'; +} from '../../../lib/entityMetadataProvider/declarations'; +import { IEntityMetadataFixedQuery, FixedQueryType } from '../../../lib/entityMetadataProvider/query'; import { TokenGenerator } from './tokenGenerator'; import { QueryTokensByCorporateID } from './tokenProvider'; import { Type } from './type'; -import { TableSettings } from '../../lib/entityMetadataProvider/table'; -import { MemorySettings } from '../../lib/entityMetadataProvider/memory'; +import { TableSettings } from '../../../lib/entityMetadataProvider/table'; +import { MemorySettings } from '../../../lib/entityMetadataProvider/memory'; import { odata, TableEntityQueryOptions } from '@azure/data-tables'; import { PostgresConfiguration, PostgresJsonEntityQuery, PostgresSettings, -} from '../../lib/entityMetadataProvider/postgres'; -import type { ApiClientGroupDisplay } from '../../interfaces/api'; +} from '../../../lib/entityMetadataProvider/postgres'; +import type { ApiClientGroupDisplay } from '../../../interfaces/api'; const type = Type; @@ -167,6 +170,19 @@ export class PersonalAccessToken implements IObjectWithDefinedKeys, ITokenEntity return apis.includes(scope.toLowerCase()); } + hasAnyScope(scope: string[]) { + if (!this.scopes) { + return false; + } + const apis = this.scopes.toLowerCase().split(','); + for (let i = 0; i < scope.length; i++) { + if (apis.includes(scope[i].toLowerCase())) { + return true; + } + } + return false; + } + hasOrganizationScope(orgName: string) { if (!this.organizationScopes) { return false; diff --git a/entities/token/tokenGenerator.ts b/business/entities/token/tokenGenerator.ts similarity index 100% rename from entities/token/tokenGenerator.ts rename to business/entities/token/tokenGenerator.ts diff --git a/entities/token/tokenProvider.ts b/business/entities/token/tokenProvider.ts similarity index 95% rename from entities/token/tokenProvider.ts rename to business/entities/token/tokenProvider.ts index 19f61926d..6ad5b13bb 100644 --- a/entities/token/tokenProvider.ts +++ b/business/entities/token/tokenProvider.ts @@ -3,10 +3,10 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { EntityMetadataBase } from '../../lib/entityMetadataProvider/entityMetadata'; +import { EntityMetadataBase } from '../../../lib/entityMetadataProvider/entityMetadata'; import { PersonalAccessToken, EntityImplementation } from './token'; import { ITokenProvider, ITokenProviderCreateOptions } from '.'; -import { IEntityMetadataFixedQuery, FixedQueryType } from '../../lib/entityMetadataProvider/query'; +import { IEntityMetadataFixedQuery, FixedQueryType } from '../../../lib/entityMetadataProvider/query'; import { Type } from './type'; const thisProviderType = Type; diff --git a/entities/token/type.ts b/business/entities/token/type.ts similarity index 68% rename from entities/token/type.ts rename to business/entities/token/type.ts index 8d70d2f71..a0ca4ea4e 100644 --- a/entities/token/type.ts +++ b/business/entities/token/type.ts @@ -3,6 +3,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { EntityMetadataType } from '../../lib/entityMetadataProvider/entityMetadata'; +import { EntityMetadataType } from '../../../lib/entityMetadataProvider/entityMetadata'; export const Type = new EntityMetadataType('Token'); diff --git a/entities/userSettings.ts b/business/entities/userSettings.ts similarity index 92% rename from entities/userSettings.ts rename to business/entities/userSettings.ts index 10a12a7a0..a8756c1c8 100644 --- a/entities/userSettings.ts +++ b/business/entities/userSettings.ts @@ -8,13 +8,13 @@ import { IEntityMetadataBaseOptions, EntityMetadataBase, IEntityMetadata, -} from '../lib/entityMetadataProvider/entityMetadata'; -import { QueryBase, IEntityMetadataFixedQuery } from '../lib/entityMetadataProvider/query'; +} from '../../lib/entityMetadataProvider/entityMetadata'; +import { QueryBase, IEntityMetadataFixedQuery } from '../../lib/entityMetadataProvider/query'; import { EntityMetadataMappings, MetadataMappingDefinition, -} from '../lib/entityMetadataProvider/declarations'; -import { PostgresConfiguration, PostgresSettings } from '../lib/entityMetadataProvider/postgres'; +} from '../../lib/entityMetadataProvider/declarations'; +import { PostgresConfiguration, PostgresSettings } from '../../lib/entityMetadataProvider/postgres'; const type = new EntityMetadataType('UserSettings'); const thisProviderType = type; @@ -33,7 +33,10 @@ class UserSettingsQueryBase extends QueryBase { } class UserSettingsQuery extends UserSettingsQueryBase { - constructor(query: Query, public parameters: T) { + constructor( + query: Query, + public parameters: T + ) { super(query); if (!this.parameters) { this.parameters = {} as T; diff --git a/features/index.ts b/business/features/index.ts similarity index 100% rename from features/index.ts rename to business/features/index.ts diff --git a/features/newRepositories/actions/createReadme.ts b/business/features/newRepositories/actions/createReadme.ts similarity index 93% rename from features/newRepositories/actions/createReadme.ts rename to business/features/newRepositories/actions/createReadme.ts index 738600562..1fb6fc636 100644 --- a/features/newRepositories/actions/createReadme.ts +++ b/business/features/newRepositories/actions/createReadme.ts @@ -3,8 +3,8 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Repository } from '../../../business'; -import { ErrorHelper } from '../../../transitional'; +import { Repository } from '../../..'; +import { ErrorHelper } from '../../../../lib/transitional'; import { setupRepositoryReadmeSubstring } from '../strings'; export async function tryCreateReadme(repository: Repository, log: string[]): Promise { diff --git a/features/newRepositories/actions/deleteFork.ts b/business/features/newRepositories/actions/deleteFork.ts similarity index 93% rename from features/newRepositories/actions/deleteFork.ts rename to business/features/newRepositories/actions/deleteFork.ts index e68aa7cef..a15ff479a 100644 --- a/features/newRepositories/actions/deleteFork.ts +++ b/business/features/newRepositories/actions/deleteFork.ts @@ -3,7 +3,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Repository } from '../../../business'; +import { Repository } from '../../..'; export async function immediatelyDeleteFork(log: string[], repository: Repository): Promise { const organization = repository.organization; diff --git a/features/newRepositories/actions/downgradeCollaborator.ts b/business/features/newRepositories/actions/downgradeCollaborator.ts similarity index 87% rename from features/newRepositories/actions/downgradeCollaborator.ts rename to business/features/newRepositories/actions/downgradeCollaborator.ts index 127826cf6..ad1698e02 100644 --- a/features/newRepositories/actions/downgradeCollaborator.ts +++ b/business/features/newRepositories/actions/downgradeCollaborator.ts @@ -3,8 +3,8 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Repository } from '../../../business'; -import { GitHubRepositoryPermission } from '../../../interfaces'; +import { Repository } from '../../..'; +import { GitHubRepositoryPermission } from '../../../../interfaces'; export async function tryDowngradeCollaborator( repository: Repository, diff --git a/features/newRepositories/actions/dropCollaborator.ts b/business/features/newRepositories/actions/dropCollaborator.ts similarity index 93% rename from features/newRepositories/actions/dropCollaborator.ts rename to business/features/newRepositories/actions/dropCollaborator.ts index 2382c1b3a..508aef375 100644 --- a/features/newRepositories/actions/dropCollaborator.ts +++ b/business/features/newRepositories/actions/dropCollaborator.ts @@ -3,7 +3,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Repository } from '../../../business'; +import { Repository } from '../../..'; export async function tryDropCollaborator( repository: Repository, diff --git a/features/newRepositories/actions/dropTeam.ts b/business/features/newRepositories/actions/dropTeam.ts similarity index 93% rename from features/newRepositories/actions/dropTeam.ts rename to business/features/newRepositories/actions/dropTeam.ts index 186a73566..62af1b5ef 100644 --- a/features/newRepositories/actions/dropTeam.ts +++ b/business/features/newRepositories/actions/dropTeam.ts @@ -3,7 +3,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Repository, Team } from '../../../business'; +import { Repository, Team } from '../../..'; export async function tryDropTeam(repository: Repository, team: Team, log: string[]): Promise { const organization = repository.organization; diff --git a/features/newRepositories/actions/index.ts b/business/features/newRepositories/actions/index.ts similarity index 100% rename from features/newRepositories/actions/index.ts rename to business/features/newRepositories/actions/index.ts diff --git a/features/newRepositories/actions/lockdown.ts b/business/features/newRepositories/actions/lockdown.ts similarity index 84% rename from features/newRepositories/actions/lockdown.ts rename to business/features/newRepositories/actions/lockdown.ts index 212537b83..de6d28b23 100644 --- a/features/newRepositories/actions/lockdown.ts +++ b/business/features/newRepositories/actions/lockdown.ts @@ -3,8 +3,8 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Repository } from '../../../business'; -import { GitHubCollaboratorAffiliationQuery } from '../../../interfaces'; +import { Repository } from '../../..'; +import { GitHubCollaboratorAffiliationQuery } from '../../../../interfaces'; import { tryDowngradeCollaborator } from './downgradeCollaborator'; import { tryDropCollaborator } from './dropCollaborator'; import { tryDropTeam } from './dropTeam'; @@ -18,9 +18,9 @@ export async function lockdownRepository( const organization = repository.organization; try { const specialPermittedTeams = new Set([ - ...organization.specialRepositoryPermissionTeams.admin, - ...organization.specialRepositoryPermissionTeams.write, - ...organization.specialRepositoryPermissionTeams.read, + ...organization.specialSystemTeams.admin, + ...organization.specialSystemTeams.write, + ...organization.specialSystemTeams.read, ]); const teamPermissions = await repository.getTeamPermissions(); for (const tp of teamPermissions) { @@ -42,7 +42,7 @@ export async function lockdownRepository( if (collaborator.login.toLowerCase() !== creatorLogin.toLowerCase()) { await tryDropCollaborator(repository, collaborator.login, log); } else { - // Downgrade the creator to only having READ access (V2) + // Downgrade the creator to only having READ access if (collaborator.permissions.admin || collaborator.permissions.push) { await tryDowngradeCollaborator(repository, collaborator.login, log); } else { diff --git a/features/newRepositories/approve.ts b/business/features/newRepositories/approve.ts similarity index 97% rename from features/newRepositories/approve.ts rename to business/features/newRepositories/approve.ts index 051711e10..ec731b651 100644 --- a/features/newRepositories/approve.ts +++ b/business/features/newRepositories/approve.ts @@ -3,10 +3,10 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Operations, Repository } from '../../business'; +import { Operations, Repository } from '../..'; import { IRepositoryMetadataProvider } from '../../entities/repositoryMetadata/repositoryMetadataProvider'; -import { ICachedEmployeeInformation, RepositoryLockdownState } from '../../interfaces'; -import { IMail } from '../../lib/mailProvider'; +import { ICachedEmployeeInformation, RepositoryLockdownState } from '../../../interfaces'; +import { IMail } from '../../../lib/mailProvider'; import { IMailToRemoveAdministrativeLock } from './interfaces'; export async function administrativeApproval( diff --git a/features/newRepositories/initializeMetadata.ts b/business/features/newRepositories/initializeMetadata.ts similarity index 84% rename from features/newRepositories/initializeMetadata.ts rename to business/features/newRepositories/initializeMetadata.ts index ef6217d42..fd53feef0 100644 --- a/features/newRepositories/initializeMetadata.ts +++ b/business/features/newRepositories/initializeMetadata.ts @@ -4,7 +4,8 @@ // import { RepositoryMetadataEntity } from '../../entities/repositoryMetadata/repositoryMetadata'; -import { GitHubRepositoryVisibility, ICorporateLink } from '../../interfaces'; +import { GitHubRepositoryVisibility, ICorporateLink } from '../../../interfaces'; +import { ErrorHelper } from '../../../lib/transitional'; import { RepositoryLockdownCreateOptions } from './interfaces'; export async function initializeRepositoryMetadata(parameters: RepositoryLockdownCreateOptions) { @@ -19,7 +20,7 @@ export async function initializeRepositoryMetadata(parameters: RepositoryLockdow instances, } = parameters; const { repository } = instances; - const { repositoryMetadataProvider } = providers; + const { insights, repositoryMetadataProvider } = providers; const { organization } = repository; try { // Repository metadata is used to lock down the security of the repository setup system. Only @@ -28,8 +29,20 @@ export async function initializeRepositoryMetadata(parameters: RepositoryLockdow let repositoryMetadata: RepositoryMetadataEntity = null; try { repositoryMetadata = await repositoryMetadataProvider.getRepositoryMetadata(String(repository.id)); - } catch (doesNotExist) { - // ignore: 404 is standard here + } catch (error) { + if (!ErrorHelper.IsNotFound(error)) { + insights?.trackException({ + exception: error, + properties: { + content: 'RepositoryMetadataProviderGetRepositoryMetadataError', + message: error.message, + repositoryId: repository.id.toString(), + repositoryName: repository.name, + organizationName: organization.name, + organizationId: organization.id.toString(), + }, + }); + } } if (repositoryMetadata) { lockdownLog.push(`Repository metadata already exists for repository ID ${repository.id}`); diff --git a/features/newRepositories/interfaces.ts b/business/features/newRepositories/interfaces.ts similarity index 86% rename from features/newRepositories/interfaces.ts rename to business/features/newRepositories/interfaces.ts index 7d5a19cea..d4c07f067 100644 --- a/features/newRepositories/interfaces.ts +++ b/business/features/newRepositories/interfaces.ts @@ -3,9 +3,11 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Operations, Organization, Repository } from '../../business'; +import { TelemetryClient } from 'applicationinsights'; + +import { Operations, Organization, Repository } from '../..'; import { IRepositoryMetadataProvider } from '../../entities/repositoryMetadata/repositoryMetadataProvider'; -import { ICorporateLink, RepositoryLockdownState } from '../../interfaces'; +import { ICorporateLink, RepositoryLockdownState } from '../../../interfaces'; export enum RepositoryLockdownCreateType { Created = 'created', @@ -13,6 +15,7 @@ export enum RepositoryLockdownCreateType { } export type RepositoryLockdownCreateProviders = { + insights: TelemetryClient; operations: Operations; repositoryMetadataProvider: IRepositoryMetadataProvider; }; @@ -73,9 +76,10 @@ export interface IMailToLockdownRepo { isForkDeleted: boolean; } -export interface INewRepositoryLockdownSystemOptions { +export type NewRepositoryLockdownSystemOptions = { + insights: TelemetryClient; operations: Operations; organization: Organization; repository: Repository; repositoryMetadataProvider: IRepositoryMetadataProvider; -} +}; diff --git a/features/newRepositories/lockdownMail.ts b/business/features/newRepositories/lockdownMail.ts similarity index 97% rename from features/newRepositories/lockdownMail.ts rename to business/features/newRepositories/lockdownMail.ts index 0fba395dc..ce4a9edbe 100644 --- a/features/newRepositories/lockdownMail.ts +++ b/business/features/newRepositories/lockdownMail.ts @@ -3,10 +3,10 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Operations, Repository } from '../../business'; -import { ICachedEmployeeInformation, ICorporateLink, RepositoryLockdownState } from '../../interfaces'; -import { IMail } from '../../lib/mailProvider'; -import getCompanySpecificDeployment from '../../middleware/companySpecificDeployment'; +import { Operations, Repository } from '../..'; +import { ICachedEmployeeInformation, ICorporateLink, RepositoryLockdownState } from '../../../interfaces'; +import { IMail } from '../../../lib/mailProvider'; +import getCompanySpecificDeployment from '../../../middleware/companySpecificDeployment'; import { ILockdownResult, IMailToLockdownRepo, RepositoryLockdownCreateType } from './interfaces'; const defaultMailTemplate = 'newrepolockdown'; diff --git a/features/newRepositories/newRepositoryLockdown.ts b/business/features/newRepositories/newRepositoryLockdown.ts similarity index 85% rename from features/newRepositories/newRepositoryLockdown.ts rename to business/features/newRepositories/newRepositoryLockdown.ts index 3ae9829de..4a5dfe90c 100644 --- a/features/newRepositories/newRepositoryLockdown.ts +++ b/business/features/newRepositories/newRepositoryLockdown.ts @@ -3,20 +3,20 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Operations, Organization, Repository } from '../../business'; +import { Operations, Organization, Repository } from '../..'; import { IRepositoryMetadataProvider } from '../../entities/repositoryMetadata/repositoryMetadataProvider'; -import { botBracket } from '../../utils'; +import { botBracket } from '../../../lib/utils'; import { ICorporateLink, RepositoryLockdownState, NoCacheNoBackground, OrganizationMembershipState, OrganizationMembershipRole, -} from '../../interfaces'; -import { CreateError } from '../../transitional'; +} from '../../../interfaces'; +import { CreateError } from '../../../lib/transitional'; import { ILockdownResult, - INewRepositoryLockdownSystemOptions, + NewRepositoryLockdownSystemOptions, IRepoPatch, RepositoryLockdownCreateOptions, RepositoryLockdownCreateType, @@ -29,8 +29,10 @@ import { setupRepositorySubstring } from './strings'; import { immediatelyDeleteFork, tryCreateReadme } from './actions'; import { repositoryLockdownStatics } from './staticFunctions'; import { lockdownRepository } from './actions/lockdown'; +import { TelemetryClient } from 'applicationinsights'; export default class NewRepositoryLockdownSystem { + insights: TelemetryClient; organization: Organization; operations: Operations; repository: Repository; @@ -40,7 +42,8 @@ export default class NewRepositoryLockdownSystem { private readonly lockdownForks: boolean; private readonly lockdownTransfers: boolean; - constructor(options: INewRepositoryLockdownSystemOptions) { + constructor(options: NewRepositoryLockdownSystemOptions) { + this.insights = options.insights; this.organization = options.organization; this.operations = options.operations; this.repository = options.repository; @@ -108,6 +111,7 @@ export default class NewRepositoryLockdownSystem { repository: this.repository, }, providers: { + insights: this.insights, repositoryMetadataProvider: this.repositoryMetadataProvider, operations: this.operations, }, @@ -162,20 +166,22 @@ export default class NewRepositoryLockdownSystem { if (!this.organization.isNewRepositoryLockdownSystemEnabled()) { return { wasLocked: false, notifyOperations: false }; } - lockdownLog.push( - `Confirmed that the ${this.organization.name} organization has opted into the new repository lockdown system` - ); - if (this.deleteForks) { - lockdownLog.push( - 'Confirmed that the delete fork feature is enabled for this org. It will supersede fork lockdown capabilities.' - ); - } - if (this.lockdownForks) { - lockdownLog.push('Confirmed that the additional fork lockdown feature is enabled for this org'); - } - if (this.lockdownTransfers) { - lockdownLog.push('Confirmed that the additional transfer lockdown feature is enabled for this org'); - } + // informationalLog.push( + // `Confirmed that the ${this.organization.name} organization has opted into the new repository lockdown system` + // ); + // if (this.deleteForks) { + // informationalLog.push( + // 'Confirmed that the delete fork feature is enabled for this org. It will supersede fork lockdown capabilities.' + // ); + // } + // if (this.lockdownForks) { + // informationalLog.push('Confirmed that the additional fork lockdown feature is enabled for this org'); + // } + // if (this.lockdownTransfers) { + // informationalLog.push( + // 'Confirmed that the additional transfer lockdown feature is enabled for this org' + // ); + // } const setupUrl = `${this.organization.absoluteBaseUrl}wizard?existingreponame=${this.repository.name}&existingrepoid=${this.repository.id}`; const isTransfer = action === RepositoryLockdownCreateType.Transferred; if (isTransfer && !this.lockdownTransfers) { @@ -198,7 +204,7 @@ export default class NewRepositoryLockdownSystem { lockdownLog.push(`Created by a bot or GitHub App: ${username}`); return { wasLocked: false, log: lockdownLog, notifyOperations: false }; } - lockdownLog.push(`Confirmed that the repository was not ${action} by a bot`); + // informationalLog.push(`Confirmed that the repository was not ${action} by a bot`); // a repository created by one of the operations accounts in the allowed list is OK and will not be locked down const systemAccounts = new Set( this.operations.systemAccountsByUsername.map((username) => username.toLowerCase()) @@ -207,11 +213,11 @@ export default class NewRepositoryLockdownSystem { lockdownLog.push(`Created by a system account: ${username}`); return { wasLocked: false, log: lockdownLog, notifyOperations: true }; } - lockdownLog.push( - `Confirmed that the repository was not ${action} by any of the system accounts: ${Array.from( - systemAccounts.values() - ).join(', ')}` - ); + // informationalLog.push( + // `Confirmed that the repository was not ${action} by any of the system accounts: ${Array.from( + // systemAccounts.values() + // ).join(', ')}` + // ); const userMembership = await this.organization.getMembership(username, NoCacheNoBackground); let userIsOrganizationOwner = false; if ( @@ -261,13 +267,13 @@ export default class NewRepositoryLockdownSystem { const patchChanges: IRepoPatch = {}; if (!isFork && !isTransfer && !this.repository.private) { - lockdownLog.push('Preparing to hide the public repository pending setup (V2)'); + // informationalLog.push('Preparing to hide the public repository pending setup'); patchChanges.private = true; } if (!isFork) { - lockdownLog.push('Updating the description and web site to point at the setup wizard (V2)'); - lockdownLog.push(`Will direct the user to ${setupUrl}`); - patchChanges.description = `${setupRepositorySubstring} ${setupUrl}`; + // informationalLog.push('Updating the description and web site to point at the setup wizard'); + // informationalLog.push(`Will direct the user to ${setupUrl}`); + patchChanges.description = setupRepositorySubstring; patchChanges.homepage = setupUrl; } if (Object.getOwnPropertyNames(patchChanges).length > 0) { @@ -280,7 +286,7 @@ export default class NewRepositoryLockdownSystem { lockdownLog.push(`Updating repository with patch ${descriptiveUpdate}`); await this.repository.update(patchChanges); } catch (hideError) { - lockdownLog.push(`Error while trying to update the new repo: ${hideError} (V2)`); + lockdownLog.push(`Error while trying to update the new repo: ${hideError}`); } } } diff --git a/features/newRepositories/selfServiceDelete.ts b/business/features/newRepositories/selfServiceDelete.ts similarity index 97% rename from features/newRepositories/selfServiceDelete.ts rename to business/features/newRepositories/selfServiceDelete.ts index 9d4374c6d..9e5fbbdb2 100644 --- a/features/newRepositories/selfServiceDelete.ts +++ b/business/features/newRepositories/selfServiceDelete.ts @@ -3,10 +3,10 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Operations, Repository } from '../../business'; +import { Operations, Repository } from '../..'; import { IRepositoryMetadataProvider } from '../../entities/repositoryMetadata/repositoryMetadataProvider'; -import { ICachedEmployeeInformation, RepositoryLockdownState } from '../../interfaces'; -import { IMail } from '../../lib/mailProvider'; +import { ICachedEmployeeInformation, RepositoryLockdownState } from '../../../interfaces'; +import { IMail } from '../../../lib/mailProvider'; export async function selfServiceDeleteLockedRepository( operations: Operations, diff --git a/features/newRepositories/staticFunctions.ts b/business/features/newRepositories/staticFunctions.ts similarity index 90% rename from features/newRepositories/staticFunctions.ts rename to business/features/newRepositories/staticFunctions.ts index 2044ce2c3..d5e225f6b 100644 --- a/features/newRepositories/staticFunctions.ts +++ b/business/features/newRepositories/staticFunctions.ts @@ -3,9 +3,9 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Repository } from '../../business'; +import { Repository } from '../..'; import { RepositoryMetadataEntity } from '../../entities/repositoryMetadata/repositoryMetadata'; -import { IndividualContext } from '../../business/user'; +import { IndividualContext } from '../../user'; import { validateUserCanSelfDeleteRepository } from './validateSelfServiceDelete'; import { validateUserCanConfigureRepository } from './validateSelfServiceSetup'; diff --git a/features/newRepositories/strings.ts b/business/features/newRepositories/strings.ts similarity index 100% rename from features/newRepositories/strings.ts rename to business/features/newRepositories/strings.ts diff --git a/features/newRepositories/validateSelfServiceDelete.ts b/business/features/newRepositories/validateSelfServiceDelete.ts similarity index 90% rename from features/newRepositories/validateSelfServiceDelete.ts rename to business/features/newRepositories/validateSelfServiceDelete.ts index d16bf6be3..e1f3cc714 100644 --- a/features/newRepositories/validateSelfServiceDelete.ts +++ b/business/features/newRepositories/validateSelfServiceDelete.ts @@ -5,11 +5,11 @@ import { DateTime } from 'luxon'; -import { Repository } from '../../business'; +import { Repository } from '../..'; import { RepositoryMetadataEntity } from '../../entities/repositoryMetadata/repositoryMetadata'; -import { RepositoryLockdownState } from '../../interfaces'; -import { IndividualContext } from '../../business/user'; -import { daysInMilliseconds } from '../../utils'; +import { RepositoryLockdownState } from '../../../interfaces'; +import { IndividualContext } from '../../user'; +import { daysInMilliseconds } from '../../../lib/utils'; export async function validateUserCanSelfDeleteRepository( repository: Repository, diff --git a/features/newRepositories/validateSelfServiceSetup.ts b/business/features/newRepositories/validateSelfServiceSetup.ts similarity index 94% rename from features/newRepositories/validateSelfServiceSetup.ts rename to business/features/newRepositories/validateSelfServiceSetup.ts index dcb08455e..edbd2cd04 100644 --- a/features/newRepositories/validateSelfServiceSetup.ts +++ b/business/features/newRepositories/validateSelfServiceSetup.ts @@ -4,8 +4,8 @@ // import { RepositoryMetadataEntity } from '../../entities/repositoryMetadata/repositoryMetadata'; -import { RepositoryLockdownState } from '../../interfaces'; -import { IndividualContext } from '../../business/user'; +import { RepositoryLockdownState } from '../../../interfaces'; +import { IndividualContext } from '../../user'; export async function validateUserCanConfigureRepository( metadata: RepositoryMetadataEntity, diff --git a/features/publicReposFastFilter.ts b/business/features/publicReposFastFilter.ts similarity index 94% rename from features/publicReposFastFilter.ts rename to business/features/publicReposFastFilter.ts index b876c57f2..e481faf57 100644 --- a/features/publicReposFastFilter.ts +++ b/business/features/publicReposFastFilter.ts @@ -3,8 +3,8 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { IProviders } from '../interfaces'; -import { Repository } from '../business'; +import { IProviders } from '../../interfaces'; +import { Repository } from '..'; // TODO: refresh occasionally. diff --git a/features/sudo/index.ts b/business/features/sudo/index.ts similarity index 90% rename from features/sudo/index.ts rename to business/features/sudo/index.ts index 8d8791024..f6eddcbcd 100644 --- a/features/sudo/index.ts +++ b/business/features/sudo/index.ts @@ -3,9 +3,9 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import type { Organization } from '../../business'; -import getCompanySpecificDeployment from '../../middleware/companySpecificDeployment'; -import { ICorporateLink, IProviders } from '../../interfaces'; +import type { Organization } from '../..'; +import getCompanySpecificDeployment from '../../../middleware/companySpecificDeployment'; +import { ICorporateLink, IProviders } from '../../../interfaces'; export interface IOrganizationSudo { isSudoer(githubLogin: string, link?: ICorporateLink): Promise; @@ -16,7 +16,10 @@ export interface IPortalSudo { } export abstract class OrganizationSudo implements IOrganizationSudo { - constructor(protected providers: IProviders, protected organization: Organization) {} + constructor( + protected providers: IProviders, + protected organization: Organization + ) {} abstract isSudoer(githubLogin: string, link?: ICorporateLink): Promise; protected isSudoEnvironmentOff() { diff --git a/features/sudo/noop.ts b/business/features/sudo/noop.ts similarity index 65% rename from features/sudo/noop.ts rename to business/features/sudo/noop.ts index 88df28d77..8db9f7c00 100644 --- a/features/sudo/noop.ts +++ b/business/features/sudo/noop.ts @@ -4,11 +4,14 @@ // import { OrganizationSudo } from '.'; -import { Organization } from '../../business'; -import { ICorporateLink, IProviders } from '../../interfaces'; +import { Organization } from '../..'; +import { ICorporateLink, IProviders } from '../../../interfaces'; export class OrganizationSudoNoop extends OrganizationSudo { - constructor(protected providers: IProviders, protected organization: Organization) { + constructor( + protected providers: IProviders, + protected organization: Organization + ) { super(providers, organization); } diff --git a/features/sudo/portal.ts b/business/features/sudo/portal.ts similarity index 94% rename from features/sudo/portal.ts rename to business/features/sudo/portal.ts index b87cd0635..dec705167 100644 --- a/features/sudo/portal.ts +++ b/business/features/sudo/portal.ts @@ -4,10 +4,10 @@ // import { IPortalSudo } from '.'; -import { Organization } from '../../business'; -import { IProviders, ICorporateLink } from '../../interfaces'; -import getCompanySpecificDeployment from '../../middleware/companySpecificDeployment'; -import { ErrorHelper } from '../../transitional'; +import { Organization } from '../..'; +import { IProviders, ICorporateLink } from '../../../interfaces'; +import getCompanySpecificDeployment from '../../../middleware/companySpecificDeployment'; +import { ErrorHelper } from '../../../lib/transitional'; abstract class PortalSudoBase { constructor(private providers: IProviders) {} diff --git a/features/sudo/securityGroup.ts b/business/features/sudo/securityGroup.ts similarity index 93% rename from features/sudo/securityGroup.ts rename to business/features/sudo/securityGroup.ts index c650ed480..81b7fdc39 100644 --- a/features/sudo/securityGroup.ts +++ b/business/features/sudo/securityGroup.ts @@ -4,9 +4,9 @@ // import { OrganizationSudo } from '.'; -import { Organization } from '../../business'; -import { IProviders, ICorporateLink } from '../../interfaces'; -import { ErrorHelper } from '../../transitional'; +import { Organization } from '../..'; +import { IProviders, ICorporateLink } from '../../../interfaces'; +import { ErrorHelper } from '../../../lib/transitional'; export const OrganizationFeatureSecurityGroupProperty = 'orgsudosecuritygroup'; diff --git a/features/sudo/sudo.md b/business/features/sudo/sudo.md similarity index 90% rename from features/sudo/sudo.md rename to business/features/sudo/sudo.md index ff567cde5..fca93b503 100644 --- a/features/sudo/sudo.md +++ b/business/features/sudo/sudo.md @@ -33,11 +33,6 @@ Overrides are available to allow the company-specific system to provide the org sudo instance for an organization, if you wish to implement a different approach, or use a different company-internal system for these decisions. -### Debug flags - -There is an environmental off-switch enabled that can turn off sudo, allowing for testing -as a regular user in local environments. That env variable name is `DEBUG_GITHUB_ORG_SUDO_OFF`. - ## portal sudo Portal sudo applies sudo for all organizations configured within the application. @@ -46,7 +41,7 @@ Used by system administrators typically. The original design was to use the sudo configuration from the first/primary GitHub org that was configured in the environment. -### Feature flag: FEATURE_FLAG_ALLOW_PORTAL_SUDO +## Feature flag: FEATURE_FLAG_ALLOW_PORTAL_SUDO > This feature is not on by default. @@ -63,7 +58,7 @@ Can be: For the security group provider, configuration should set `SUDO_PORTAL_SECURITY_GROUP_ID` to the security group ID to use. -### Debug flags +## Debug flags: portal sudo Two environment variables designed for development work exist: diff --git a/features/sudo/teams.ts b/business/features/sudo/teams.ts similarity index 91% rename from features/sudo/teams.ts rename to business/features/sudo/teams.ts index f15a48536..ed4758cbb 100644 --- a/features/sudo/teams.ts +++ b/business/features/sudo/teams.ts @@ -4,9 +4,9 @@ // import { OrganizationSudo } from '.'; -import { Organization } from '../../business'; -import { IProviders, ICorporateLink, GitHubTeamRole, ITeamMembershipRoleState } from '../../interfaces'; -import { ErrorHelper } from '../../transitional'; +import { Organization } from '../..'; +import { IProviders, ICorporateLink, GitHubTeamRole, ITeamMembershipRoleState } from '../../../interfaces'; +import { ErrorHelper } from '../../../lib/transitional'; export class OrganizationSudoGitHubTeams extends OrganizationSudo { constructor(providers: IProviders, organization: Organization) { diff --git a/features/teamMemberToMaintainerUpgrade.ts b/business/features/teamMemberToMaintainerUpgrade.ts similarity index 96% rename from features/teamMemberToMaintainerUpgrade.ts rename to business/features/teamMemberToMaintainerUpgrade.ts index f3f3f0d68..5ed5daff4 100644 --- a/features/teamMemberToMaintainerUpgrade.ts +++ b/business/features/teamMemberToMaintainerUpgrade.ts @@ -3,18 +3,18 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Team } from '../business'; -import { Operations } from '../business'; -import { IndividualContext } from '../business/user'; -import { addArrayToSet } from '../utils'; -import { IMail } from '../lib/mailProvider'; +import { Team } from '..'; +import { Operations } from '..'; +import { IndividualContext } from '../user'; +import { addArrayToSet } from '../../lib/utils'; +import { IMail } from '../../lib/mailProvider'; import { NoCacheNoBackground, ITeamMembershipRoleState, OrganizationMembershipState, GitHubTeamRole, -} from '../interfaces'; -import { ErrorHelper } from '../transitional'; +} from '../../interfaces'; +import { ErrorHelper } from '../../lib/transitional'; interface ISelfServiceAllowedResult { currentMaintainerCount: number; diff --git a/business/githubApps/index.ts b/business/githubApps/index.ts deleted file mode 100644 index 7f1ddfdec..000000000 --- a/business/githubApps/index.ts +++ /dev/null @@ -1,151 +0,0 @@ -// -// Copyright (c) Microsoft. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. -// - -import { IReposApplication } from '../../interfaces'; -import { CreateError } from '../../transitional'; - -import Debug from 'debug'; -const debug = Debug('github:tokens'); - -export enum AppPurpose { - Data = 'Data', - CustomerFacing = 'CustomerFacing', - Operations = 'Operations', - BackgroundJobs = 'BackgroundJobs', // "secondary" / "default" fallback - Updates = 'Updates', - Security = 'Security', - ActionsData = 'ActionsData', -} - -export interface ICustomAppPurpose { - isCustomAppPurpose: boolean; // basic type check - id: string; - name: string; - getForOrganizationName?(organizationName: string): IGitHubAppConfiguration; - getApplicationConfigurationForInitialization?(): IGitHubAppConfiguration; -} - -export type AppPurposeTypes = AppPurpose | ICustomAppPurpose; - -export abstract class CustomAppPurpose implements ICustomAppPurpose { - get isCustomAppPurpose() { - return true; - } - constructor(public id: string, public name: string) {} -} - -export class CustomAppPurposeOrganizationVariance extends CustomAppPurpose { - fallbackIfNotConfiguredOrganizationName = false; - constructor(public id: string, public name: string, private configurations: IGitHubAppConfiguration[]) { - super(id, name); - } - getForOrganizationName(organizationName: string) { - const configuration = this.configurations.find( - (c) => c.specificOrganizationName.toLowerCase() === organizationName.toLowerCase() - ); - if (!configuration && this.fallbackIfNotConfiguredOrganizationName === false) { - throw CreateError.NotFound(`No configuration found for organization ${organizationName}`); - } - return configuration || this.configurations[0]; - } -} - -export class CustomAppPurposeSingleConfiguration extends CustomAppPurpose { - constructor(public id: string, public name: string, private configuration: IGitHubAppConfiguration) { - super(id, name); - } - - getApplicationConfigurationForInitialization() { - return this.configuration; - } -} - -export const DefinedAppPurposes = [ - AppPurpose.Data, - AppPurpose.CustomerFacing, - AppPurpose.Operations, - AppPurpose.BackgroundJobs, - AppPurpose.Updates, - AppPurpose.Security, - AppPurpose.ActionsData, -]; - -// export const GitHubAppPurposesExemptFromAllRepositoriesSelection = [AppPurpose.Onboarding]; - -const appPurposeToConfigurationName = { - [AppPurpose.Data]: 'data', - [AppPurpose.CustomerFacing]: 'ui', - [AppPurpose.Operations]: 'operations', - [AppPurpose.BackgroundJobs]: 'jobs', - [AppPurpose.Updates]: 'updates', - [AppPurpose.Security]: 'security', - [AppPurpose.ActionsData]: 'actions', -}; - -export function getAppPurposeId(purpose: AppPurposeTypes) { - if ((purpose as ICustomAppPurpose).isCustomAppPurpose === true) { - return (purpose as ICustomAppPurpose).id; - } - const asPurpose = purpose as AppPurpose; - const id = appPurposeToConfigurationName[asPurpose]; - if (!id) { - throw new Error(`No configuration name for purpose ${asPurpose}`); - } - return id; -} - -export class GitHubAppPurposes { - private static _instance: GitHubAppPurposes = new GitHubAppPurposes(); - - static get AllAvailableAppPurposes() { - debug(`Retrieving all available purposes (${this._instance._purposes.length})`); - return this._instance._purposes; - } - - static RegisterCustomPurpose(purpose: ICustomAppPurpose) { - debug(`Registering custom purpose ${purpose.id} (${purpose.name})`); - if (purpose.isCustomAppPurpose !== true) { - throw new Error('Purpose must have `isCustomAppPurpose` set to true'); - } - if ( - (this._instance._purposes as ICustomAppPurpose[]) - .filter((p) => (p as ICustomAppPurpose)?.isCustomAppPurpose === true) - .find((p) => p.id === purpose.id) - ) { - throw new Error(`Purpose with ID ${purpose.id} already registered`); - } - this._instance._purposes.push(purpose); - } - - private _purposes: AppPurposeTypes[]; - - constructor() { - this._purposes = [...DefinedAppPurposes]; - } -} - -export enum GitHubAppAuthenticationType { - ForceSpecificInstallation = 'force', - BestAvailable = 'best', -} - -export interface IGitHubAppConfiguration { - clientId?: string; - clientSecret?: string; - appId?: number; - appKey?: string; - appKeyFile?: string; - webhookSecret?: string; - slug?: string; - description?: string; - baseUrl?: string; - - specificOrganizationName?: string; -} - -export interface IGitHubAppsOptions { - app: IReposApplication; - configurations: Map; -} diff --git a/business/graphManager.ts b/business/graphManager.ts index eb3624736..a0e0cdc2c 100644 --- a/business/graphManager.ts +++ b/business/graphManager.ts @@ -18,7 +18,7 @@ import { IPagedCrossOrganizationCacheOptions, GitHubRepositoryPermission, } from '../interfaces'; -import { isPermissionBetterThan } from '../transitional'; +import { isPermissionBetterThan } from '../lib/transitional'; interface ILocalLinksCache { updated: moment.Moment; diff --git a/business/operations/administration.ts b/business/operations/administration.ts index 6cc984d31..139f7e830 100644 --- a/business/operations/administration.ts +++ b/business/operations/administration.ts @@ -4,7 +4,7 @@ // import { IProviders } from '../../interfaces'; -import { ErrorHelper } from '../../transitional'; +import { ErrorHelper } from '../../lib/transitional'; export async function isAuthorizedSystemAdministrator( providers: IProviders, diff --git a/business/operations/core.ts b/business/operations/core.ts index 6b4e0fdd4..161e2164e 100644 --- a/business/operations/core.ts +++ b/business/operations/core.ts @@ -3,9 +3,14 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { OrganizationSetting } from '../../entities/organizationSettings/organizationSetting'; -import { GitHubAppAuthenticationType, AppPurpose, ICustomAppPurpose, AppPurposeTypes } from '../githubApps'; -import { GitHubTokenManager } from '../githubApps/tokenManager'; +import { OrganizationSetting } from '../entities/organizationSettings/organizationSetting'; +import { + GitHubAppAuthenticationType, + AppPurpose, + ICustomAppPurpose, + AppPurposeTypes, +} from '../../lib/github/appPurposes'; +import { GitHubTokenManager } from '../../lib/github/tokenManager'; import { IProviders, ICacheDefaultTimes, @@ -19,22 +24,48 @@ import { throwIfNotGitHubCapable, throwIfNotCapable, IOperationsCentralOperationsToken, - IAuthorizationHeaderValue, + AuthorizationHeaderValue, SiteConfiguration, + ExecutionEnvironment, + IPagedCacheOptions, + ICacheOptionsWithPurpose, } from '../../interfaces'; import { RestLibrary } from '../../lib/github'; -import { CreateError } from '../../transitional'; -import { wrapError } from '../../utils'; +import { CreateError } from '../../lib/transitional'; +import { wrapError } from '../../lib/utils'; import { Account } from '../account'; import GitHubApplication from '../application'; import Debug from 'debug'; const debugGitHubTokens = Debug('github:tokens'); +const symbolCost = Symbol('cost'); +const symbolHeaders = Symbol('headers'); + +export function symbolizeApiResponse(response: any): T { + if (response && response.headers) { + response[symbolHeaders] = response.headers; + delete response.headers; + } + if (response && response.cost) { + response[symbolCost] = response.cost; + delete response.cost; + } + return response; +} + +export function getApiSymbolMetadata(response: any) { + if (response) { + return { headers: response[symbolHeaders], cost: response[symbolCost] }; + } + throw CreateError.ParameterRequired('response'); +} + export interface IOperationsCoreOptions { github: RestLibrary; providers: IProviders; baseUrl?: string; + executionEnvironment: ExecutionEnvironment; } export enum CacheDefault { @@ -61,6 +92,7 @@ export enum CacheDefault { teamDetailStaleSeconds = 'teamDetailStaleSeconds', orgRepoWebhooksStaleSeconds = 'orgRepoWebhooksStaleSeconds', teamRepositoryPermissionStaleSeconds = 'teamRepositoryPermissionStaleSeconds', + defaultStaleSeconds = 'defaultStaleSeconds', } // defaults could move to configuration alternatively @@ -88,6 +120,7 @@ const defaults: ICacheDefaultTimes = { [CacheDefault.teamDetailStaleSeconds]: 60 * 60 * 2 /* 2h */, [CacheDefault.orgRepoWebhooksStaleSeconds]: 60 * 60 * 8 /* 8h */, [CacheDefault.teamRepositoryPermissionStaleSeconds]: 0 /* 0m */, + [CacheDefault.defaultStaleSeconds]: 60 /* 1m */, }; export const DefaultPageSize = 100; @@ -106,6 +139,46 @@ export function getPageSize(operations: IOperationsInstance, options?: IOptionWi return DefaultPageSize; } +export function createCacheOptions( + operations: IOperationsInstance, + options?: ICacheOptions, + cacheDefault: CacheDefault = CacheDefault.defaultStaleSeconds +) { + const cacheOptions: ICacheOptions = { + maxAgeSeconds: getMaxAgeSeconds(operations, cacheDefault, options, 60), + }; + if (options.backgroundRefresh !== undefined) { + cacheOptions.backgroundRefresh = options.backgroundRefresh; + } + return cacheOptions; +} + +export function createPagedCacheOptions( + operations: IOperationsInstance, + options?: IPagedCacheOptions, + cacheDefault: CacheDefault = CacheDefault.defaultStaleSeconds +) { + const cacheOptions: IPagedCacheOptions = { + maxAgeSeconds: getMaxAgeSeconds(operations, cacheDefault, options, 60), + }; + if (options.pageRequestDelay !== undefined) { + cacheOptions.pageRequestDelay = options.pageRequestDelay; + } + if (options.backgroundRefresh !== undefined) { + cacheOptions.backgroundRefresh = options.backgroundRefresh; + } + return cacheOptions; +} + +export function popPurpose(options: ICacheOptionsWithPurpose, defaultPurpose: AppPurposeTypes) { + if (options.purpose) { + const purpose = options.purpose; + delete options.purpose; + return purpose; + } + return defaultPurpose; +} + export function getMaxAgeSeconds( operations: IOperationsInstance, cacheDefault: CacheDefault, @@ -195,7 +268,7 @@ export abstract class OperationsCore async getAccountByUsername(username: string, options?: ICacheOptions): Promise { options = options || {}; const operations = throwIfNotGitHubCapable(this); - const centralOperations = throwIfNotCapable( + const ops = throwIfNotCapable( this, CoreCapability.GitHubCentralOperations ); @@ -212,10 +285,9 @@ export abstract class OperationsCore cacheOptions.backgroundRefresh = options.backgroundRefresh; } try { - const getHeaderFunction = centralOperations.getCentralOperationsToken(); - const authorizationHeader = await getHeaderFunction(AppPurpose.Data); + const getHeaderFunction = ops.getPublicAuthorizationToken(); const entity = await operations.github.call( - authorizationHeader, + getHeaderFunction, 'users.getByUsername', parameters, cacheOptions @@ -335,17 +407,16 @@ export abstract class OperationsCore organizationName: string, organizationSettings: OrganizationSetting, legacyOwnerToken: string, - centralOperationsFallbackToken: string, appAuthenticationType: GitHubAppAuthenticationType, purpose: AppPurposeTypes - ): Promise { + ): Promise { const customPurpose = purpose as ICustomAppPurpose; const isCustomPurpose = customPurpose?.isCustomAppPurpose === true; if ( !isCustomPurpose && !this.tokenManager.organizationSupportsAnyPurpose(organizationName, organizationSettings) ) { - const legacyTokenValue = legacyOwnerToken || centralOperationsFallbackToken; + const legacyTokenValue = legacyOwnerToken; if (!legacyTokenValue) { throw new Error( `Organization ${organizationName} is not configured with a GitHub app, Personal Access Token ownerToken configuration value, or a fallback central operations token for the ${ @@ -356,7 +427,7 @@ export abstract class OperationsCore return { value: `token ${legacyTokenValue}`, purpose: null, - source: legacyOwnerToken ? 'legacyOwnerToken' : 'centralOperationsFallbackToken', + source: 'legacyOwnerToken', }; } if (!purpose) { diff --git a/business/operations/index.ts b/business/operations/index.ts index c2124a153..7040e423c 100644 --- a/business/operations/index.ts +++ b/business/operations/index.ts @@ -5,31 +5,33 @@ import axios from 'axios'; import throat from 'throat'; +import { shuffle } from 'lodash'; import { Account } from '../account'; import { GraphManager } from '../graphManager'; -import { IGitHubOrganizationResponse, Organization } from '../organization'; -import { GitHubTokenManager } from '../githubApps/tokenManager'; +import { GitHubOrganizationResponse, Organization } from '../organization'; +import { GitHubTokenManager } from '../../lib/github/tokenManager'; import RenderHtmlMail from '../../lib/emailRender'; -import { wrapError, sortByCaseInsensitive } from '../../utils'; +import { wrapError, sortByCaseInsensitive } from '../../lib/utils'; import { Repository } from '../repository'; import { RestLibrary } from '../../lib/github'; import { + AppPurpose, AppPurposeTypes, getAppPurposeId, GitHubAppAuthenticationType, GitHubAppPurposes, IGitHubAppConfiguration, -} from '../githubApps'; +} from '../../lib/github/appPurposes'; import { OrganizationFeature, OrganizationSetting, -} from '../../entities/organizationSettings/organizationSetting'; -import { OrganizationSettingProvider } from '../../entities/organizationSettings/organizationSettingProvider'; +} from '../entities/organizationSettings/organizationSetting'; +import { OrganizationSettingProvider } from '../entities/organizationSettings/organizationSettingProvider'; import { IMail } from '../../lib/mailProvider'; import { ILinkProvider } from '../../lib/linkProviders'; import { ICacheHelper } from '../../lib/caching'; -import { createPortalSudoInstance, IPortalSudo } from '../../features'; +import { createPortalSudoInstance, IPortalSudo } from '../features'; import { CacheDefault, getMaxAgeSeconds, IOperationsCoreOptions, OperationsCore } from './core'; import { linkAccounts as linkAccountsMethod } from './link'; import { sendTerminatedAccountMail as sendTerminatedAccountMailMethod } from './unlinkMail'; @@ -42,7 +44,7 @@ import { ICreateLinkOptions, ICrossOrganizationMembershipByOrganization, ICrossOrganizationTeamMembership, - IGetAuthorizationHeader, + GetAuthorizationHeader, IGitHubAppInstallation, IMapPlusMetaCost, IOperationsCentralOperationsToken, @@ -56,18 +58,22 @@ import { IOperationsTemplates, IPagedCrossOrganizationCacheOptions, IPromisedLinks, - IPurposefulGetAuthorizationHeader, + PurposefulGetAuthorizationHeader, ISupportedLinkTypeOutcome, IUnlinkMailStatus, NoCacheNoBackground, SupportedLinkType, UnlinkPurpose, + type LinkEvent, + type UnlinkEvent, } from '../../interfaces'; -import { CreateError, ErrorHelper } from '../../transitional'; +import { CreateError, ErrorHelper } from '../../lib/transitional'; import { Team } from '../team'; -import { IRepositoryMetadataProvider } from '../../entities/repositoryMetadata/repositoryMetadataProvider'; +import { IRepositoryMetadataProvider } from '../entities/repositoryMetadata/repositoryMetadataProvider'; import { isAuthorizedSystemAdministrator } from './administration'; import type { ConfigGitHubOrganizationsSpecializedList } from '../../config/github.organizations.types'; +import { type GitHubTokenType, getGitHubTokenTypeFromValue } from '../../lib/github/appTokens'; +import getCompanySpecificDeployment from '../../middleware/companySpecificDeployment'; export * from './core'; @@ -86,13 +92,7 @@ export interface ICrossOrganizationMembersResult extends Map {} export interface IOperationsOptions extends IOperationsCoreOptions { - // cacheProvider: ICacheHelper; - // config: any; github: RestLibrary; - // insights: TelemetryClient; - // linkProvider: ILinkProvider; - // mailAddressProvider: IMailAddressProvider; - // mailProvider: IMailProvider; repositoryMetadataProvider: IRepositoryMetadataProvider; } @@ -102,6 +102,12 @@ export type GetInvisibleOrganizationOptions = { storeInstanceByName?: boolean; }; +type CreateOrganizationOptions = { + settings: OrganizationSetting; + appAuthenticationType: GitHubAppAuthenticationType; + asUncontrolledPublicOnly?: boolean; +}; + export class Operations extends OperationsCore implements @@ -122,7 +128,7 @@ export class Operations private _invisibleOrganizations: Map; private _uncontrolledOrganizations: Map; private _organizationOriginalNames: any; - private _organizationNamesWithAuthorizationHeaders: Map; + private _organizationNamesWithAuthorizationHeaders: Map; private _defaultPageSize: number; private _organizationIds: Map; private _organizationSettings: OrganizationSetting[]; @@ -178,10 +184,10 @@ export class Operations } } this._tokenManager = new GitHubTokenManager({ + operations: this, configurations: purposesToConfigurations, - app: this.providers.app, + executionEnvironment: options.executionEnvironment, }); - GitHubTokenManager.RegisterManagerForOperations(this, this._tokenManager); this._dynamicOrganizationIds = new Set(); this._organizationSettings = []; } @@ -342,41 +348,46 @@ export class Operations return Array.from(this._organizationIds.keys()); } - private createOrganization( - name: string, - settings: OrganizationSetting, - centralOperationsFallbackToken: string, - appAuthenticationType: GitHubAppAuthenticationType - ): Organization { + private createOrganization(name: string, options: CreateOrganizationOptions): Organization { name = name.toLowerCase(); + if (!options) { + throw CreateError.ParameterRequired('options'); + } + const { settings, appAuthenticationType, asUncontrolledPublicOnly } = options; if (!settings) { - throw new Error(`This application is not configured for the ${name} organization`); + throw CreateError.InvalidParameters( + `This application does not have configuration information for the ${name} organization` + ); } const ownerToken = settings.getOwnerToken(); const hasDynamicSettings = this._dynamicOrganizationIds && settings.organizationId && this._dynamicOrganizationIds.has(Number(settings.organizationId)); + let configuredGetAuthorizationHeader: GetAuthorizationHeader = this.getAuthorizationHeader.bind( + this, + name, + settings, + ownerToken, + appAuthenticationType + ); + let forcedGetAuthorizationHeader: GetAuthorizationHeader = this.getAuthorizationHeader.bind( + this, + name, + settings, + ownerToken, + GitHubAppAuthenticationType.ForceSpecificInstallation + ); + if (!ownerToken && asUncontrolledPublicOnly) { + configuredGetAuthorizationHeader = this.getPublicAuthorizationToken(); + forcedGetAuthorizationHeader = configuredGetAuthorizationHeader; + } return new Organization( this, name, settings, - this.getAuthorizationHeader.bind( - this, - name, - settings, - ownerToken, - centralOperationsFallbackToken, - appAuthenticationType - ), - this.getAuthorizationHeader.bind( - this, - name, - settings, - ownerToken, - centralOperationsFallbackToken, - GitHubAppAuthenticationType.ForceSpecificInstallation - ), + configuredGetAuthorizationHeader, + forcedGetAuthorizationHeader, hasDynamicSettings ); } @@ -385,7 +396,6 @@ export class Operations if (!this._organizations) { const organizations = new Map(); const names = this.organizationNames; - const centralOperationsToken = this.config.github.operations.centralOperationsToken; for (let i = 0; i < names.length; i++) { const name = names[i]; let settings: OrganizationSetting = null; @@ -398,12 +408,10 @@ export class Operations settings = dos; } } - const organization = this.createOrganization( - name, + const organization = this.createOrganization(name, { settings, - centralOperationsToken, - GitHubAppAuthenticationType.BestAvailable - ); + appAuthenticationType: GitHubAppAuthenticationType.BestAvailable, + }); organizations.set(name, organization); } this._organizations = organizations; @@ -422,13 +430,10 @@ export class Operations for (let i = 0; i < list.length; i++) { const settings = list[i]; if (settings && settings.name && settings.name.toLowerCase() === lowercase) { - const centralOperationsToken = this.config.github.operations.centralOperationsToken; - return this.createOrganization( - lowercase, - OrganizationSetting.CreateFromStaticSettings(settings), - centralOperationsToken, - GitHubAppAuthenticationType.BestAvailable - ); + return this.createOrganization(lowercase, { + settings: OrganizationSetting.CreateFromStaticSettings(settings), + appAuthenticationType: GitHubAppAuthenticationType.BestAvailable, + }); } } } @@ -444,12 +449,10 @@ export class Operations } getUnconfiguredOrganization(settings: OrganizationSetting): Organization { - return this.createOrganization( - settings.organizationName.toLowerCase(), + return this.createOrganization(settings.organizationName.toLowerCase(), { settings, - null, - GitHubAppAuthenticationType.BestAvailable - ); + appAuthenticationType: GitHubAppAuthenticationType.BestAvailable, + }); } // An invisible organization does not appear in the cross-organization @@ -480,7 +483,10 @@ export class Operations dynamicSettings = options.settings; } const authenticationType = options?.authenticationType || GitHubAppAuthenticationType.BestAvailable; - const organization = this.createOrganization(name, dynamicSettings, null, authenticationType); + const organization = this.createOrganization(name, { + settings: dynamicSettings, + appAuthenticationType: authenticationType, + }); if (!options || options?.storeInstanceByName) { this._invisibleOrganizations.set(name, organization); } @@ -498,13 +504,12 @@ export class Operations } const emptySettings = new OrganizationSetting(); emptySettings.operationsNotes = `Uncontrolled Organization - ${organizationName}`; - const centralOperationsToken = this.config.github.operations.centralOperationsToken; - const org = this.createOrganization( - organizationName, - emptySettings, - centralOperationsToken, - GitHubAppAuthenticationType.ForceSpecificInstallation - ); + const asUncontrolledPublicOnly = true; + const org = this.createOrganization(organizationName, { + settings: emptySettings, + appAuthenticationType: GitHubAppAuthenticationType.ForceSpecificInstallation, + asUncontrolledPublicOnly, + }); this._uncontrolledOrganizations.set(organizationName, org); org.uncontrolled = true; return org; @@ -512,18 +517,19 @@ export class Operations getPublicOnlyAccessOrganization(organizationName: string, organizationId?: number): Organization { organizationName = organizationName.toLowerCase(); - const emptySettings = new OrganizationSetting(); - emptySettings.operationsNotes = `Uncontrolled public organization - ${organizationName}`; const publicAccessToken = this.config.github.operations.publicAccessToken; if (!publicAccessToken) { - throw new Error('not configured for public read-only tokens'); + throw CreateError.InvalidParameters('not configured for public read-only tokens'); } - const org = this.createOrganization( - organizationName, - emptySettings, + const emptySettings = OrganizationSetting.CreateEmptyWithOldToken( publicAccessToken, - GitHubAppAuthenticationType.ForceSpecificInstallation + `Uncontrolled public organization - ${organizationName}`, + organizationId ); + const org = this.createOrganization(organizationName, { + settings: emptySettings, + appAuthenticationType: GitHubAppAuthenticationType.ForceSpecificInstallation, + }); this._uncontrolledOrganizations.set(organizationName, org); org.uncontrolled = true; return org; @@ -599,14 +605,14 @@ export class Operations get organizationNamesWithAuthorizationHeaders() { if (!this._organizationNamesWithAuthorizationHeaders) { - const tokens = new Map(); + const tokens = new Map(); const visited = new Set(); for (const entry of this._organizationSettings) { const lowercase = entry.organizationName.toLowerCase(); if (entry.active && !visited.has(lowercase) && !entry.hasFeature(OrganizationFeature.Invisible)) { visited.add(lowercase); const orgInstance = this.getOrganization(lowercase); - const token = orgInstance.getAuthorizationHeader(); + const token = orgInstance.getAuthorizationHeader(AppPurpose.Data); tokens.set(lowercase, token); } } @@ -784,10 +790,6 @@ export class Operations return this.config.brand.operationsMail; } - getInfrastructureNotificationsMail(): string { - return this.config.brand.infrastructureNotificationsMail || this.getOperationsMailAddress(); - } - getLinksNotificationMailAddress(): string { return this.config.notifications.linksMailAddress || this.getOperationsMailAddress(); } @@ -885,6 +887,22 @@ export class Operations } async getRepoById(repoId: number, options?: ICacheOptions): Promise { + const { repositoryCacheProvider } = this.providers; + if (repositoryCacheProvider) { + try { + const cachedRepository = await repositoryCacheProvider.getRepository(String(repoId)); + if (cachedRepository?.organizationId) { + const organization = this.getOrganizationById(Number(cachedRepository.organizationId)); + return organization.repository(cachedRepository.repositoryName); + } + } catch (error) { + if (ErrorHelper.IsNotFound(error)) { + console.log(`Repository ${repoId} not found in the cache: ${error}`); + } else { + console.log(`Repository ${repoId} error retrieving from cache: ${error}`); + } + } + } const cacheOptions = options || { maxAgeSeconds: this.defaults.crossOrgsReposStaleSecondsPerOrg, }; @@ -906,10 +924,46 @@ export class Operations } } - async getOrganizationProfileById( + async getOrganizationProfileById(id: number, options?: ICacheOptions): Promise { + options = options || {}; + if (!id) { + throw new Error('Must provide a repository ID to retrieve the repository.'); + } + const organization = this._organizationIds.get(id); + return this._getOrganizationProfileById(id, organization ? id : null, options); + } + + async getOrganizationPublicProfileById( id: number, options?: ICacheOptions - ): Promise { + ): Promise { + options = options || {}; + if (!id) { + throw new Error('Must provide a repository ID to retrieve the repository.'); + } + let lookupId: number | null = this._organizationIds.get(id) ? id : null; + if (lookupId) { + const allIdsExcludingOrg = this.getOrganizationIds().filter((orgId) => orgId !== id); + const shuffledIds = shuffle(allIdsExcludingOrg); + if (shuffledIds.length > 0) { + lookupId = shuffledIds[0]; + } + } + if (lookupId === null) { + throw CreateError.InvalidParameters( + 'This approach requires configuring at least two organizations (getOrganizationPublicProfileById).' + ); + } + return this._getOrganizationProfileById(id, lookupId, options); + } + + private async _getOrganizationProfileById( + id: number, + lookupUsingIdOrCentralToken: number | null, + options?: ICacheOptions + ): Promise { + // EMU note: you need to use an EMU-installed app vs public... + // Cache note: this will be a cache miss if you switch between public/non-public entrypoints options = options || {}; if (!id) { throw new Error('Must provide a repository ID to retrieve the repository.'); @@ -923,13 +977,15 @@ export class Operations if (options.backgroundRefresh !== undefined) { cacheOptions.backgroundRefresh = options.backgroundRefresh; } + const organization = this._organizationIds.get(lookupUsingIdOrCentralToken); + let header: GetAuthorizationHeader = null; + if (organization) { + header = organization.getAuthorizationHeader(AppPurpose.Data) as GetAuthorizationHeader; + } else { + header = this.getPublicAuthorizationToken(); + } try { - const entity = await this.github.request( - this.getCentralOperationsToken(), - 'GET /organizations/:id', - parameters, - cacheOptions - ); + const entity = await this.github.request(header, 'GET /organizations/:id', parameters, cacheOptions); return entity; } catch (error) { if (error.status && error.status === 404) { @@ -1242,11 +1298,17 @@ export class Operations // Eventually link/unlink should move from context into operations here to centralize more than just the events - async fireLinkEvent(value): Promise { + async fireLinkEvent(value: LinkEvent): Promise { + const companySpecific = getCompanySpecificDeployment(); + companySpecific?.events?.linking?.onLink && companySpecific.events.linking.onLink(this.providers, value); await fireEvent(this.config, 'link', value); } - async fireUnlinkEvent(value): Promise { + async fireUnlinkEvent(value: UnlinkEvent): Promise { + const corporateId = value?.aad?.id; + const companySpecific = getCompanySpecificDeployment(); + companySpecific?.events?.linking?.onUnlink && + companySpecific.events.linking.onUnlink(this.providers, corporateId); await fireEvent(this.config, 'unlink', value); } @@ -1279,19 +1341,32 @@ export class Operations return false; } - getCentralOperationsToken(): IGetAuthorizationHeader { - const func = getCentralOperationsAuthorizationHeader.bind(null, this) as IGetAuthorizationHeader; - return func; + getPublicReadOnlyStaticToken(): GetAuthorizationHeader { + const { config } = this.providers; + if (config?.github?.operations?.publicAccessToken) { + const capturedToken = config.github.operations.publicAccessToken; + return async () => { + return { + value: `token ${capturedToken}`, + purpose: null, + source: 'public read-only token', + }; + }; + } + throw CreateError.InvalidParameters('No configured read-only static token'); } - getPublicReadOnlyToken(): IGetAuthorizationHeader { - const func = getReadOnlyAuthorizationHeader.bind(null, this) as IGetAuthorizationHeader; - return func; + getPublicAuthorizationToken(): GetAuthorizationHeader { + try { + return this._tokenManager.getAuthorizationHeaderForAnyApp.bind(this._tokenManager); + } catch (error) { + return this.getPublicReadOnlyStaticToken(); + } } getAccount(id: string) { const entity = { id }; - return new Account(entity, this, getCentralOperationsAuthorizationHeader.bind(null, this)); + return new Account(entity, this, this.getPublicAuthorizationToken.bind(this)); } async getAccountWithDetailsAndLink(id: string): Promise { @@ -1300,14 +1375,27 @@ export class Operations } async getAuthenticatedAccount(token: string): Promise { + // Returns an account instance based on the account identified in the token. const github = this.github; const parameters = {}; + const fullToken = `token ${token}`; + let tokenType: GitHubTokenType = null; try { - const entity = await github.post(`token ${token}`, 'users.getAuthenticated', parameters); - const account = new Account(entity, this, getCentralOperationsAuthorizationHeader.bind(null, this)); + tokenType = getGitHubTokenTypeFromValue(fullToken); + } catch (error) { + // ignoring any issue here on identifying the type of token + } + try { + const entity = await github.post(fullToken, 'users.getAuthenticated', parameters); + const account = new Account(entity, this, this.getPublicAuthorizationToken.bind(this)); return account; } catch (error) { - throw wrapError(error, 'Could not get details about the authenticated account'); + throw wrapError( + error, + `Could not get details about the authenticated account${ + tokenType ? ' using token type "' + tokenType + '"' : '.' + }` + ); } } @@ -1421,42 +1509,6 @@ async function fireEvent(config, configurationName, value): Promise { - return { - value: `token ${capturedToken}`, - purpose: null, - source: 'public read-only token', - }; - }; - } else { - throw new Error('No public read-only token configured.'); - } -} - -export function getCentralOperationsAuthorizationHeader(self: Operations): IPurposefulGetAuthorizationHeader { - const s = (self || this) as Operations; - if (s.config.github && s.config.github.operations && s.config.github.operations.centralOperationsToken) { - const capturedToken = s.config.github.operations.centralOperationsToken; - return async () => { - return { - value: `token ${capturedToken}`, - purpose: null, // legacy - source: 'central operations token', - }; - }; - } else if (s.getOrganizations().length === 0) { - throw new Error('No central operations token nor any organizations configured.'); - } - // Fallback to the first configured organization as a convenience - // CONSIDER: would randomizing the organization be better, or a priority based on known-rate limit remaining? - const firstOrganization = s.getOrganizations()[0]; - return firstOrganization.getAuthorizationHeader(); -} - function crossOrganizationResults(operations: Operations, results, keyProperty) { keyProperty = keyProperty || 'id'; const map: IMapPlusMetaCost = new Map(); diff --git a/business/operations/link.ts b/business/operations/link.ts index b41e2a6c2..d4a269857 100644 --- a/business/operations/link.ts +++ b/business/operations/link.ts @@ -12,7 +12,7 @@ import { ICorporateLink, } from '../../interfaces'; import getCompanySpecificDeployment from '../../middleware/companySpecificDeployment'; -import { CreateError, ErrorHelper, setImmediateAsync } from '../../transitional'; +import { CreateError, ErrorHelper, setImmediateAsync } from '../../lib/transitional'; export async function linkAccounts( operations: Operations, diff --git a/business/operations/unlinkMail.ts b/business/operations/unlinkMail.ts index 78d4601d9..50522e05f 100644 --- a/business/operations/unlinkMail.ts +++ b/business/operations/unlinkMail.ts @@ -7,7 +7,7 @@ import { Operations } from '.'; import { Account } from '../account'; import { UnlinkPurpose, IUnlinkMailStatus, ICachedEmployeeInformation } from '../../interfaces'; import getCompanySpecificDeployment from '../../middleware/companySpecificDeployment'; -import { assertUnreachable } from '../../transitional'; +import { assertUnreachable } from '../../lib/transitional'; export async function sendTerminatedAccountMail( operations: Operations, @@ -64,7 +64,7 @@ export async function sendTerminatedAccountMail( break; case UnlinkPurpose.Termination: subjectPrefix = '[UNLINKED] '; - headline = `${displayName} is not an active employee`; + headline = `${displayName} has had their GitHub access offboarded`; break; case UnlinkPurpose.Unknown: default: diff --git a/business/organization.ts b/business/organization.ts index b25945d04..ef8fa2b65 100644 --- a/business/organization.ts +++ b/business/organization.ts @@ -10,29 +10,30 @@ import { OrganizationMember } from './organizationMember'; import { Team } from './team'; import { Repository } from './repository'; -import { wrapError } from '../utils'; +import { wrapError } from '../lib/utils'; import { StripGitHubEntity } from '../lib/github/restApi'; import { GitHubResponseType } from '../lib/github/endpointEntities'; -import { AppPurpose, AppPurposeTypes } from './githubApps'; +import { AppPurpose, AppPurposeTypes } from '../lib/github/appPurposes'; import { OrganizationFeature, OrganizationSetting, - SpecialTeam, -} from '../entities/organizationSettings/organizationSetting'; -import { createOrganizationSudoInstance, IOrganizationSudo } from '../features'; + SystemTeam, +} from './entities/organizationSettings/organizationSetting'; +import { createOrganizationSudoInstance, IOrganizationSudo } from './features'; import { CacheDefault, getMaxAgeSeconds, getPageSize, OperationsCore } from './operations/core'; import { CoreCapability, GitHubAuditLogEntry, + GitHubOrganizationInvite, GitHubRepositoryVisibility, IAccountBasics, IAddOrganizationMembershipOptions, - IAuthorizationHeaderValue, + AuthorizationHeaderValue, ICacheOptions, ICacheOptionsWithPurpose, ICorporateLink, ICreateRepositoryResult, - IGetAuthorizationHeader, + type GetAuthorizationHeader, IGetOrganizationAuditLogOptions, IGetOrganizationMembersOptions, IGitHubAccountDetails, @@ -48,7 +49,7 @@ import { IOrganizationMemberPair, IOrganizationMembership, IPagedCacheOptions, - IPurposefulGetAuthorizationHeader, + type PurposefulGetAuthorizationHeader, IReposError, IReposRestRedisCacheCost, NoCacheNoBackground, @@ -58,14 +59,17 @@ import { OrganizationMembershipTwoFactorFilter, throwIfNotCapable, throwIfNotGitHubCapable, + GitHubRepositoryDetails, } from '../interfaces'; -import { CreateError, ErrorHelper } from '../transitional'; +import { CreateError, ErrorHelper } from '../lib/transitional'; import { jsonError } from '../middleware'; import getCompanySpecificDeployment from '../middleware/companySpecificDeployment'; import { ConfigGitHubTemplates } from '../config/github.templates.types'; -import { GitHubTokenManager } from './githubApps/tokenManager'; +import { GitHubTokenManager } from '../lib/github/tokenManager'; import { OrganizationProjects } from './projects'; import { OrganizationDomains } from './domains'; +import { OrganizationCopilot } from './organizationCopilot'; +import { OrganizationProperties } from './organizationProperties'; interface IGetMembersParameters { org: string; @@ -101,7 +105,7 @@ export interface IGitHubOrganizationPlanResponse { space: number; } -export interface IGitHubOrganizationResponse { +export type GitHubOrganizationResponse = { avatar_url?: string; billing_email?: string; blog?: string; @@ -135,8 +139,73 @@ export interface IGitHubOrganizationResponse { type: string; updated_at: string; url: string; +}; + +// the only fields we want in this type based on GitHubOrganizationResponse are avatar_url, blog, company, created_at, description, email, id, location, login, name, updated_at +export type GitHubOrganizationResponseSanitized = Pick< + GitHubOrganizationResponse, + | 'avatar_url' + | 'blog' + | 'company' + | 'created_at' + | 'description' + | 'email' + | 'id' + | 'location' + | 'login' + | 'name' + | 'updated_at' +>; + +const sanitizedFields = [ + 'avatar_url', + 'blog', + 'company', + 'created_at', + 'description', + 'email', + 'id', + 'location', + 'login', + 'name', + 'updated_at', +]; + +export function getOrganizationDetailsSanitized( + details: GitHubOrganizationResponse +): GitHubOrganizationResponseSanitized { + if (details) { + const sanitized = {} as GitHubOrganizationResponseSanitized; + for (const field of sanitizedFields) { + if (details[field]) { + sanitized[field] = details[field]; + } + } + return sanitized; + } } +export interface RunnerData { + busy: boolean; + id: number; + name: string; + os: string; + status: string; + labels: { + id: number; + name: string; + type: string; + }; +} + +export interface IGitHubOrganizationRunners { + total_count: number; + runners: RunnerData[]; +} +type CreateRepositoryEntityById = Partial & Pick; +type CreateRepositoryEntityByName = Partial & Pick; +type CreateRepositoryEntity = CreateRepositoryEntityById | CreateRepositoryEntityByName; + export class Organization { private _name: string; private _baseUrl: string; @@ -144,18 +213,21 @@ export class Organization { private _nativeManagementUrl: string; private _operations: IOperationsInstance; - private _getAuthorizationHeader: IPurposefulGetAuthorizationHeader; - private _getSpecificAuthorizationHeader: IPurposefulGetAuthorizationHeader; + private _getAuthorizationHeader: PurposefulGetAuthorizationHeader; + private _getSpecificAuthorizationHeader: PurposefulGetAuthorizationHeader; private _usesGitHubApp: boolean; private _settings: OrganizationSetting; - private _entity: IGitHubOrganizationResponse; + private _entity: GitHubOrganizationResponse; private _organizationSudo: IOrganizationSudo; private _projects: OrganizationProjects; private _domains: OrganizationDomains; + private _copilot: OrganizationCopilot; + private _customProperties: OrganizationProperties; + id: number; uncontrolled: boolean; @@ -163,8 +235,8 @@ export class Organization { operations: IOperationsInstance, name: string, settings: OrganizationSetting, - getAuthorizationHeader: IPurposefulGetAuthorizationHeader, - getSpecificAuthorizationHeader: IPurposefulGetAuthorizationHeader, + getAuthorizationHeader: PurposefulGetAuthorizationHeader, + getSpecificAuthorizationHeader: PurposefulGetAuthorizationHeader, public hasDynamicSettings: boolean ) { this._name = settings.organizationName || name; @@ -251,7 +323,7 @@ export class Organization { ); } - return values; + return values as object; } getManagementApproach() { @@ -288,6 +360,28 @@ export class Organization { return this._projects; } + get copilot() { + if (!this._copilot) { + this._copilot = new OrganizationCopilot( + this, + this._getSpecificAuthorizationHeader.bind(this), + this._operations + ); + } + return this._copilot; + } + + get customProperties() { + if (!this._customProperties) { + this._customProperties = new OrganizationProperties( + this, + this._getSpecificAuthorizationHeader.bind(this), + this._operations + ); + } + return this._customProperties; + } + get domains() { if (!this._domains) { this._domains = new OrganizationDomains( @@ -325,7 +419,7 @@ export class Organization { return tokenManager.getRateLimitInformation(purpose, this); } - repository(name: string, optionalEntity?) { + repository(name: string, optionalEntity?: CreateRepositoryEntity) { const entity = Object.assign({}, optionalEntity || {}, { name, }); @@ -356,15 +450,24 @@ export class Organization { cacheOptions.backgroundRefresh = options.backgroundRefresh; } try { - const entity = await operations.github.request( - this.authorize(AppPurpose.Data), - 'GET /repositories/:id', - parameters, - cacheOptions - ); + let entity: any = null; + if ((cacheOptions as any)?.noConditionalRequests === true) { + entity = await operations.github.requestAsPost( + this.authorize(AppPurpose.Data), + 'GET /repositories/:id', + parameters + ); + } else { + entity = await operations.github.request( + this.authorize(AppPurpose.Data), + 'GET /repositories/:id', + parameters, + cacheOptions + ); + } if (entity.owner.id !== this.id) { throw CreateError.NotFound( - `Repository ID ${parameters.id} has a different owner of ${entity.owner.login} instead of ${this.name}. It has been relocated and will be treated as a 404.` + `Repository ID ${id} has a different owner of ${entity.owner.login} instead of ${this.name}. It has been relocated and will be treated as a 404.` ); } return this.repositoryFromEntity(entity); @@ -411,6 +514,29 @@ export class Organization { return repositories; } + async getOrgRunners(options?: ICacheOptions): Promise { + options = options || {}; + const operations = throwIfNotGitHubCapable(this._operations); + const github = operations.github; + const orgName = this.name; + const parameters = { + orgName, + }; + const cacheOptions: ICacheOptions = { + maxAgeSeconds: 1, // getMaxAgeSeconds(operations, CacheDefault.accountDetailStaleSeconds, options), + }; + const runnerData = await operations.github.request( + this.authorize(AppPurpose.ActionsData), + 'GET /orgs/:orgName/actions/runners', + parameters, + cacheOptions + ); + return { + runners: runnerData.runners, + total_count: runnerData.total_count, + }; + } + get priority(): string { return this._settings.properties['priority'] || 'secondary'; } @@ -427,10 +553,6 @@ export class Organization { return this._settings.hasFeature(OrganizationFeature.Hidden) || false; } - get pilot_program() { - return this._settings.properties['1es']; - } - get createRepositoriesOnGitHub(): boolean { return this._settings.hasFeature(OrganizationFeature.CreateNativeRepositories) || false; } @@ -472,7 +594,11 @@ export class Organization { } get broadAccessTeams(): number[] { - return this.getSpecialTeam(SpecialTeam.Everyone, 'everyone membership'); + return this.getSystemTeam(SystemTeam.Everyone, 'everyone membership'); + } + + get openAccessTeams(): number[] { + return this.getSystemTeam(SystemTeam.OpenAccess, 'open access'); } get invitationTeam(): Team { @@ -484,7 +610,7 @@ export class Organization { } get systemSudoersTeam(): Team { - const teams = this.getSpecialTeam(SpecialTeam.GlobalSudo, 'system sudoers'); + const teams = this.getSystemTeam(SystemTeam.GlobalSudo, 'system sudoers'); if (teams.length > 1) { throw new Error('Multiple system sudoer teams are not supported.'); } @@ -496,7 +622,7 @@ export class Organization { } get sudoersTeam(): Team { - const teams = this.getSpecialTeam(SpecialTeam.Sudo, 'organization sudoers'); + const teams = this.getSystemTeam(SystemTeam.Sudo, 'organization sudoers'); if (teams.length > 1) { throw new Error('Multiple sudoer teams are not supported.'); } @@ -510,16 +636,16 @@ export class Organization { return this._settings; } - get specialRepositoryPermissionTeams() { + get specialSystemTeams() { return { - read: this.getSpecialTeam(SpecialTeam.SystemRead, 'read everything'), - write: this.getSpecialTeam(SpecialTeam.SystemWrite, 'write everything'), - admin: this.getSpecialTeam(SpecialTeam.SystemAdmin, 'administer everything'), + read: this.getSystemTeam(SystemTeam.SystemRead, 'read everything'), + write: this.getSystemTeam(SystemTeam.SystemWrite, 'write everything'), + admin: this.getSystemTeam(SystemTeam.SystemAdmin, 'administer everything'), }; } - getAuthorizationHeader(): IPurposefulGetAuthorizationHeader { - return this._getAuthorizationHeader; + getAuthorizationHeader(purpose: AppPurposeTypes): PurposefulGetAuthorizationHeader { + return purpose ? this._getAuthorizationHeader.bind(this, purpose) : this._getAuthorizationHeader; } async getUserDetailsByLogin(login: string, purpose?: AppPurposeTypes): Promise { @@ -592,7 +718,13 @@ export class Organization { teamIds.push(broadAccessTeams[i]); // is the actual ID, not the team object } } - const specialTeams = this.specialRepositoryPermissionTeams; + const openAccessTeams = this.openAccessTeams; + if (openAccessTeams) { + for (let i = 0; i < openAccessTeams.length; i++) { + teamIds.push(openAccessTeams[i]); // is the actual ID, not the team object + } + } + const specialTeams = this.specialSystemTeams; const keys = Object.getOwnPropertyNames(specialTeams); keys.forEach((type) => { const values = specialTeams[type]; @@ -625,12 +757,12 @@ export class Organization { ); } - async getRepositoryCreateGitHubToken(): Promise { + async getRepositoryCreateGitHubToken(): Promise { // This method leaks/releases the owner token. In the future a more crisp // way of accomplishing this without exposing the token should be created. // The function name is specific to the intended use instead of a general- // purpose token name. - const token = await (this.authorize(AppPurpose.Operations) as IGetAuthorizationHeader)(); + const token = await (this.authorize(AppPurpose.Operations) as GetAuthorizationHeader)(); token.source = 'repository create token'; return token; } @@ -674,7 +806,7 @@ export class Organization { } } - async getDetails(): Promise { + async getDetails(): Promise { const operations = throwIfNotGitHubCapable(this._operations); const parameters = { org: this.name, @@ -685,7 +817,7 @@ export class Organization { this.id = entity.id; } this._entity = entity; - return entity as IGitHubOrganizationResponse; + return entity as GitHubOrganizationResponse; } catch (error) { throw wrapError(error, `Could not get details about the ${this.name} organization: ${error.message}`); } @@ -710,6 +842,42 @@ export class Organization { return metadata; } + async getTeamById(id: number, options?: ICacheOptions): Promise { + options = options || {}; + const operations = throwIfNotGitHubCapable(this._operations); + const cacheOptions = { + maxAgeSeconds: getMaxAgeSeconds(operations, CacheDefault.orgTeamDetailsStaleSeconds, options), + backgroundRefresh: false, + }; + if (options.backgroundRefresh !== undefined) { + cacheOptions.backgroundRefresh = options.backgroundRefresh; + } + const orgId = this.id; + if (!orgId) { + throw CreateError.InvalidParameters('The organization ID is not available.'); + } + const parameters = { + org_id: orgId, + team_id: id, + }; + try { + const entity = await operations.github.request( + this.authorize(AppPurpose.Data), + 'GET /organizations/:org_id/team/:team_id', + parameters, + cacheOptions + ); + return this.teamFromEntity(entity); + } catch (error) { + if (error.status && error.status === 404) { + throw CreateError.NotFound( + `The GitHub team with the ID ${id} could not be found for organization ${this.name} with ID ${orgId}.` + ); + } + throw error; + } + } + async getTeamFromSlug(slug: string, options?: ICacheOptions): Promise { options = options || {}; const operations = throwIfNotGitHubCapable(this._operations); @@ -1126,7 +1294,7 @@ export class Organization { const getAuthorizationHeader = this._getAuthorizationHeader.bind( this, AppPurpose.Data - ) as IGetAuthorizationHeader; + ) as GetAuthorizationHeader; const github = operations.github; const parameters: IGetMembersParameters = { org: this.name, @@ -1241,7 +1409,7 @@ export class Organization { const getAuthorizationHeader = this._getAuthorizationHeader.bind( this, AppPurpose.Data - ) as IGetAuthorizationHeader; + ) as GetAuthorizationHeader; const teamEntities = await github.collections.getOrgTeams(getAuthorizationHeader, parameters, caching); const teams = common.createInstances(this, this.teamFromEntity, teamEntities); return teams; @@ -1267,12 +1435,12 @@ export class Organization { if (queryCache?.supportsOrganizationMembership) { try { if (!optionalId) { - const centralOps = operationsWithCapability( + const ops = operationsWithCapability( operations, CoreCapability.GitHubCentralOperations ); - if (centralOps) { - const account = await centralOps.getAccountByUsername(login); + if (ops) { + const account = await ops.getAccountByUsername(login); optionalId = account.id.toString(); } } @@ -1284,7 +1452,7 @@ export class Organization { } } - async getMembershipInvitations(): Promise { + async getMembershipInvitations(): Promise { const operations = throwIfNotGitHubCapable(this._operations); const parameters = { org: this.name, @@ -1295,7 +1463,7 @@ export class Organization { 'orgs.listPendingInvitations', parameters ); - return invitations; + return invitations as GitHubOrganizationInvite[]; } catch (error) { if (error.status == /* loose */ 404) { return null; @@ -1304,19 +1472,16 @@ export class Organization { } } - private authorize(purpose: AppPurpose): IGetAuthorizationHeader { - const getAuthorizationHeader = this._getAuthorizationHeader.bind( - this, - purpose - ) as IGetAuthorizationHeader; + private authorize(purpose: AppPurpose): GetAuthorizationHeader { + const getAuthorizationHeader = this._getAuthorizationHeader.bind(this, purpose) as GetAuthorizationHeader; return getAuthorizationHeader; } - private authorizeSpecificPurpose(purpose: AppPurposeTypes): IGetAuthorizationHeader { + private authorizeSpecificPurpose(purpose: AppPurposeTypes): GetAuthorizationHeader { const getAuthorizationHeader = this._getSpecificAuthorizationHeader.bind( this, purpose - ) as IGetAuthorizationHeader; + ) as GetAuthorizationHeader; return getAuthorizationHeader; } @@ -1448,7 +1613,7 @@ export class Organization { return this.team(entity.id, entity); } - repositoryFromEntity(entity): Repository { + repositoryFromEntity(entity: CreateRepositoryEntity): Repository { return this.repository(entity.name, entity); } @@ -1458,23 +1623,15 @@ export class Organization { return { settings, operations }; } - private getSpecialTeam(specialTeam: SpecialTeam, friendlyName: string, throwIfMissing?: boolean): number[] { - let teamId: number = null; - for (const entry of this._settings.specialTeams) { - if (entry.specialTeam === specialTeam) { - teamId = entry.teamId; - break; - } - } - if (throwIfMissing) { + private getSystemTeam(teamType: SystemTeam, friendlyName: string, throwIfMissing?: boolean): number[] { + const allOrgSystemTeams = this._settings.specialTeams; + const matchingSystemTeamTypes = allOrgSystemTeams.filter((t) => t.specialTeam === teamType); + const teams: number[] = matchingSystemTeamTypes.map((t) => t.teamId); + if (throwIfMissing && teams.length === 0) { throw new Error( - `Missing configured organization "${this.name}" special team ${specialTeam} - ${friendlyName}` + `Missing configured organization "${this.name}" special team ${teamType} - ${friendlyName}` ); } - const teams: number[] = []; - if (teamId) { - teams.push(teamId); - } return teams; } diff --git a/business/organizationCopilot.ts b/business/organizationCopilot.ts new file mode 100644 index 000000000..8081ba09f --- /dev/null +++ b/business/organizationCopilot.ts @@ -0,0 +1,126 @@ +// +// Copyright (c) Microsoft. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +import { + GetAuthorizationHeader, + ICacheOptions, + IOperationsInstance, + IPagedCacheOptions, + PurposefulGetAuthorizationHeader, + throwIfNotGitHubCapable, +} from '../interfaces'; +import type { CollectionCopilotSeatsOptions } from '../lib/github/collections'; +import { AppPurpose, AppPurposeTypes } from '../lib/github/appPurposes'; +import { CacheDefault, getMaxAgeSeconds, getPageSize, symbolizeApiResponse } from './operations/core'; +import { Organization } from './organization'; +import { HttpMethod } from '../lib/github'; + +export type CopilotSeatData = { + assignee: { + avatar_url: string; + id: number; + login: string; + }; + created_at: string; + updated_at: string; + last_activity_at: string; + last_activity_editor: string; +}; + +export type CopilotDailyBreakdown = { + language: string; + editor: string; + suggestions_count: number; + acceptances_count: number; + lines_suggested: number; + lines_accepted: number; + active_users: number; +}; + +export type CopilotDailySummary = { + day: string; + total_suggestions_count: number; + total_acceptances_count: number; + total_lines_suggested: number; + total_lines_accepted: number; + total_active_users: number; + breakdown: CopilotDailyBreakdown[]; +}; + +export type OrganizationCopilotSummary = CopilotDailySummary[]; + +export class OrganizationCopilot { + constructor( + private organization: Organization, + private getSpecificAuthorizationHeader: PurposefulGetAuthorizationHeader, + private operations: IOperationsInstance + ) {} + + async getSeatActivity( + options?: IPagedCacheOptions, + appPurpose: AppPurposeTypes = AppPurpose.Data + ): Promise { + options = options || {}; + const operations = throwIfNotGitHubCapable(this.operations); + const getAuthorizationHeader = this.getSpecificAuthorizationHeader.bind( + this, + appPurpose + ) as GetAuthorizationHeader; + const github = operations.github; + const parameters: CollectionCopilotSeatsOptions = { + org: this.organization.name, + per_page: getPageSize(operations), + }; + const caching = { + maxAgeSeconds: getMaxAgeSeconds(operations, CacheDefault.orgMembersStaleSeconds, options), + backgroundRefresh: true, + pageRequestDelay: options.pageRequestDelay, + }; + if (options && options.backgroundRefresh === false) { + caching.backgroundRefresh = false; + } + // (caching as any).pageLimit = 10; + const seats = (await github.collections.getOrganizationCopilotSeats( + getAuthorizationHeader, + parameters, + caching + )) as CopilotSeatData[]; + return seats; + } + + async getDailyActivitySummary( + options?: ICacheOptions, + appPurpose: AppPurposeTypes = AppPurpose.Data + ): Promise { + options = options || {}; + const operations = throwIfNotGitHubCapable(this.operations); + const getAuthorizationHeader = this.getSpecificAuthorizationHeader.bind( + this, + appPurpose + ) as GetAuthorizationHeader; + const github = operations.github; + const parameters = { + org: this.organization.name, + }; + const caching = { + maxAgeSeconds: getMaxAgeSeconds(operations, CacheDefault.orgMembersStaleSeconds, options), + backgroundRefresh: true, + }; + if (options && options.backgroundRefresh === false) { + caching.backgroundRefresh = false; + } + try { + const result: OrganizationCopilotSummary = await github.request( + getAuthorizationHeader, + 'GET /orgs/:org/copilot/usage', + parameters, + caching + ); + return symbolizeApiResponse(result); + } catch (error) { + throw error; + } + } +} diff --git a/business/organizationProperties.ts b/business/organizationProperties.ts new file mode 100644 index 000000000..36080e05b --- /dev/null +++ b/business/organizationProperties.ts @@ -0,0 +1,198 @@ +// +// Copyright (c) Microsoft. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +import { + ICacheOptions, + ICacheOptionsWithPurpose, + GetAuthorizationHeader, + IOperationsInstance, + PurposefulGetAuthorizationHeader, + PagedCacheOptionsWithPurpose, + throwIfNotGitHubCapable, +} from '../interfaces'; +import { HttpMethod } from '../lib/github'; +import { CreateError } from '../lib/transitional'; +import { AppPurpose, AppPurposeTypes } from '../lib/github/appPurposes'; +import { + CacheDefault, + createPagedCacheOptions, + getMaxAgeSeconds, + getPageSize, + popPurpose, + symbolizeApiResponse, +} from './operations/core'; +import { Organization } from './organization'; + +export enum CustomPropertyValueType { + String = 'string', + SingleSelect = 'single_select', +} + +export type OrganizationCustomPropertyEntity = { + property_name: string; + value_type: CustomPropertyValueType; + required: boolean; + description?: string; + default_value?: string; + allowed_values?: string[]; +}; + +export type OrganizationCustomPropertySetPropertyValue = { + property_name: string; + value: string; +}; + +type CreateOrUpdateResponse = { + properties: OrganizationCustomPropertyEntity[]; +}; + +export class OrganizationProperties { + private _defaultPurpose = AppPurpose.Data; + + constructor( + private organization: Organization, + private getSpecificAuthorizationHeader: PurposefulGetAuthorizationHeader, + private operations: IOperationsInstance + ) {} + + private authorize(purpose: AppPurposeTypes): GetAuthorizationHeader | string { + const getAuthorizationHeader = this.getSpecificAuthorizationHeader.bind( + this, + purpose + ) as GetAuthorizationHeader; + return getAuthorizationHeader; + } + + async getCustomProperties( + options?: PagedCacheOptionsWithPurpose + ): Promise { + options = options || {}; + const operations = throwIfNotGitHubCapable(this.operations); + const { github } = operations; + const purpose = popPurpose(options, this._defaultPurpose); + const parameters = { + org: this.organization.name, + per_page: getPageSize(operations), + }; + const cacheOptions = createPagedCacheOptions(operations, options); + try { + const entities = await github.collections.collectAllPagesViaHttpGet< + any, + OrganizationCustomPropertyEntity + >( + this.authorize(purpose), + 'orgCustomProps', + 'GET /orgs/:org/properties/schema', + parameters, + cacheOptions + ); + return symbolizeApiResponse(entities); + } catch (error) { + throw error; + } + } + + async getCustomProperty( + propertyName: string, + options?: ICacheOptionsWithPurpose + ): Promise { + options = options || {}; + const operations = throwIfNotGitHubCapable(this.operations); + const { github } = operations; + if (!propertyName) { + throw CreateError.InvalidParameters('propertyName'); + } + const purpose = popPurpose(options, this._defaultPurpose); + const parameters = { + org: this.organization.name, + custom_property_name: propertyName, + }; + const cacheOptions: ICacheOptions = { + maxAgeSeconds: getMaxAgeSeconds(operations, CacheDefault.accountDetailStaleSeconds, options, 60), + }; + if (options.backgroundRefresh !== undefined) { + cacheOptions.backgroundRefresh = options.backgroundRefresh; + } + try { + const entity = (await github.request( + this.authorize(purpose), + 'GET /orgs/:org/properties/schema/:custom_property_name', + parameters, + cacheOptions + )) as OrganizationCustomPropertyEntity; + return symbolizeApiResponse(entity); + } catch (error) { + throw error; + } + } + + async deleteProperty(propertyName: string, purpose?: AppPurposeTypes): Promise { + const operations = throwIfNotGitHubCapable(this.operations); + const parameters = { + org: this.organization.name, + custom_property_name: propertyName, + }; + await operations.github.restApi( + this.authorize(purpose || this._defaultPurpose), + HttpMethod.Delete, + '/orgs/:org/properties/schema/:custom_property_name', + parameters + ); + } + + async createOrUpdate( + properties: OrganizationCustomPropertyEntity[], + purpose?: AppPurposeTypes + ): Promise { + const operations = throwIfNotGitHubCapable(this.operations); + const parameters = { + org: this.organization.name, + properties, + }; + const res = (await operations.github.restApi( + this.authorize(purpose || this._defaultPurpose), + HttpMethod.Patch, + '/orgs/:org/properties/schema', + parameters + )) as CreateOrUpdateResponse; + return res.properties; + } + + async createOrUpdateRepositoriesProperties( + organizationRepositoryNames: string[], + propertiesAndValues: Record, + purpose?: AppPurposeTypes + ): Promise { + const operations = throwIfNotGitHubCapable(this.operations); + if (organizationRepositoryNames.length > 30) { + throw CreateError.InvalidParameters( + 'GitHub has a hard limit of 30 repositories to update in a single patch' + ); + } + const parameters = { + org: this.organization.name, + properties: setPropertiesRecordToArray(propertiesAndValues), + repository_names: organizationRepositoryNames, + }; + (await operations.github.restApi( + this.authorize(purpose || this._defaultPurpose), + HttpMethod.Patch, + '/orgs/:org/properties/values', + parameters + )) as CreateOrUpdateResponse; + } +} + +function setPropertiesRecordToArray(propertiesAndValues: Record) { + const keys = Object.getOwnPropertyNames(propertiesAndValues); + const properties: OrganizationCustomPropertySetPropertyValue[] = []; + for (const key of keys) { + properties.push({ + property_name: key, + value: propertiesAndValues[key], + }); + } + return properties; +} diff --git a/business/project.ts b/business/project.ts index e1fb54c80..54571be82 100644 --- a/business/project.ts +++ b/business/project.ts @@ -3,13 +3,13 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { AppPurpose, AppPurposeTypes } from './githubApps'; +import { AppPurpose, AppPurposeTypes } from '../lib/github/appPurposes'; import { Organization, Repository } from '.'; import { IOperationsInstance, - IPurposefulGetAuthorizationHeader, + PurposefulGetAuthorizationHeader, throwIfNotGitHubCapable, - IGetAuthorizationHeader, + GetAuthorizationHeader, } from '../interfaces'; import { decorateIterable, @@ -17,7 +17,7 @@ import { IteratorResponse, PaginationPageSizeOptions, } from './iterable'; -import { CreateError, DefaultGraphqlPageSize } from '../transitional'; +import { CreateError, DefaultGraphqlPageSize } from '../lib/transitional'; import { OrganizationProjects } from './projects'; import { OrganizationProjectView } from './projectView'; @@ -117,8 +117,8 @@ export class OrganizationProject { private _projects: OrganizationProjects; private _operations: IOperationsInstance; - private _getAuthorizationHeader: IPurposefulGetAuthorizationHeader; - private _getSpecificAuthorizationHeader: IPurposefulGetAuthorizationHeader; + private _getAuthorizationHeader: PurposefulGetAuthorizationHeader; + private _getSpecificAuthorizationHeader: PurposefulGetAuthorizationHeader; private _purpose: AppPurpose; private _id: string; @@ -126,8 +126,8 @@ export class OrganizationProject { constructor( organizationProjects: OrganizationProjects, operations: IOperationsInstance, - getAuthorizationHeader: IPurposefulGetAuthorizationHeader, - getSpecificAuthorizationHeader: IPurposefulGetAuthorizationHeader, + getAuthorizationHeader: PurposefulGetAuthorizationHeader, + getSpecificAuthorizationHeader: PurposefulGetAuthorizationHeader, projectId: string ) { this._getAuthorizationHeader = getAuthorizationHeader; @@ -413,19 +413,16 @@ export class OrganizationProject { } } - private authorize(purpose: AppPurpose = this._purpose): IGetAuthorizationHeader { - const getAuthorizationHeader = this._getAuthorizationHeader.bind( - this, - purpose - ) as IGetAuthorizationHeader; + private authorize(purpose: AppPurpose = this._purpose): GetAuthorizationHeader { + const getAuthorizationHeader = this._getAuthorizationHeader.bind(this, purpose) as GetAuthorizationHeader; return getAuthorizationHeader; } - private authorizeSpecificPurpose(purpose: AppPurposeTypes): IGetAuthorizationHeader | string { + private authorizeSpecificPurpose(purpose: AppPurposeTypes): GetAuthorizationHeader | string { const getAuthorizationHeader = this._getSpecificAuthorizationHeader.bind( this, purpose - ) as IGetAuthorizationHeader; + ) as GetAuthorizationHeader; return getAuthorizationHeader; } } diff --git a/business/projectView.ts b/business/projectView.ts index 0ab645ae8..b4386b4ec 100644 --- a/business/projectView.ts +++ b/business/projectView.ts @@ -3,13 +3,13 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { AppPurpose, AppPurposeTypes } from './githubApps'; +import { AppPurpose, AppPurposeTypes } from '../lib/github/appPurposes'; import { Organization } from '.'; import { IOperationsInstance, - IPurposefulGetAuthorizationHeader, + PurposefulGetAuthorizationHeader, throwIfNotGitHubCapable, - IGetAuthorizationHeader, + GetAuthorizationHeader, } from '../interfaces'; import { decorateIterable, @@ -17,7 +17,7 @@ import { IteratorResponse, PaginationPageSizeOptions, } from './iterable'; -import { DefaultGraphqlPageSize } from '../transitional'; +import { DefaultGraphqlPageSize } from '../lib/transitional'; import { OrganizationProject, ProjectViewEssentials } from './project'; type ProjectViewDetails = { @@ -30,8 +30,8 @@ export class OrganizationProjectView { private _project: OrganizationProject; private _operations: IOperationsInstance; - private _getAuthorizationHeader: IPurposefulGetAuthorizationHeader; - private _getSpecificAuthorizationHeader: IPurposefulGetAuthorizationHeader; + private _getAuthorizationHeader: PurposefulGetAuthorizationHeader; + private _getSpecificAuthorizationHeader: PurposefulGetAuthorizationHeader; private _purpose: AppPurpose; private _id: string; @@ -41,8 +41,8 @@ export class OrganizationProjectView { constructor( organizationProject: OrganizationProject, operations: IOperationsInstance, - getAuthorizationHeader: IPurposefulGetAuthorizationHeader, - getSpecificAuthorizationHeader: IPurposefulGetAuthorizationHeader, + getAuthorizationHeader: PurposefulGetAuthorizationHeader, + getSpecificAuthorizationHeader: PurposefulGetAuthorizationHeader, projectId: string, essentials?: ProjectViewEssentials ) { @@ -217,19 +217,16 @@ export class OrganizationProjectView { // } // } - private authorize(purpose: AppPurpose = this._purpose): IGetAuthorizationHeader { - const getAuthorizationHeader = this._getAuthorizationHeader.bind( - this, - purpose - ) as IGetAuthorizationHeader; + private authorize(purpose: AppPurpose = this._purpose): GetAuthorizationHeader { + const getAuthorizationHeader = this._getAuthorizationHeader.bind(this, purpose) as GetAuthorizationHeader; return getAuthorizationHeader; } - private authorizeSpecificPurpose(purpose: AppPurposeTypes): IGetAuthorizationHeader | string { + private authorizeSpecificPurpose(purpose: AppPurposeTypes): GetAuthorizationHeader | string { const getAuthorizationHeader = this._getSpecificAuthorizationHeader.bind( this, purpose - ) as IGetAuthorizationHeader; + ) as GetAuthorizationHeader; return getAuthorizationHeader; } } diff --git a/business/projects.ts b/business/projects.ts index f010b3aaa..60cc6b8e6 100644 --- a/business/projects.ts +++ b/business/projects.ts @@ -3,13 +3,13 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { AppPurpose, AppPurposeTypes } from './githubApps'; +import { AppPurpose, AppPurposeTypes } from '../lib/github/appPurposes'; import { Organization } from '.'; import { IOperationsInstance, - IPurposefulGetAuthorizationHeader, + PurposefulGetAuthorizationHeader, throwIfNotGitHubCapable, - IGetAuthorizationHeader, + GetAuthorizationHeader, } from '../interfaces'; import { decorateIterable, @@ -17,7 +17,7 @@ import { IteratorResponse, PaginationPageSizeOptions, } from './iterable'; -import { DefaultGraphqlPageSize } from '../transitional'; +import { DefaultGraphqlPageSize } from '../lib/transitional'; import { OrganizationProject } from './project'; type ProjectResponse = { @@ -35,15 +35,15 @@ export class OrganizationProjects { private _organization: Organization; private _operations: IOperationsInstance; - private _getAuthorizationHeader: IPurposefulGetAuthorizationHeader; - private _getSpecificAuthorizationHeader: IPurposefulGetAuthorizationHeader; + private _getAuthorizationHeader: PurposefulGetAuthorizationHeader; + private _getSpecificAuthorizationHeader: PurposefulGetAuthorizationHeader; private _purpose: AppPurpose; constructor( organization: Organization, operations: IOperationsInstance, - getAuthorizationHeader: IPurposefulGetAuthorizationHeader, - getSpecificAuthorizationHeader: IPurposefulGetAuthorizationHeader + getAuthorizationHeader: PurposefulGetAuthorizationHeader, + getSpecificAuthorizationHeader: PurposefulGetAuthorizationHeader ) { this._getAuthorizationHeader = getAuthorizationHeader; this._getSpecificAuthorizationHeader = getSpecificAuthorizationHeader; @@ -134,19 +134,16 @@ export class OrganizationProjects { } } - private authorize(purpose: AppPurpose = this._purpose): IGetAuthorizationHeader { - const getAuthorizationHeader = this._getAuthorizationHeader.bind( - this, - purpose - ) as IGetAuthorizationHeader; + private authorize(purpose: AppPurpose = this._purpose): GetAuthorizationHeader { + const getAuthorizationHeader = this._getAuthorizationHeader.bind(this, purpose) as GetAuthorizationHeader; return getAuthorizationHeader; } - private authorizeSpecificPurpose(purpose: AppPurposeTypes): IGetAuthorizationHeader | string { + private authorizeSpecificPurpose(purpose: AppPurposeTypes): GetAuthorizationHeader | string { const getAuthorizationHeader = this._getSpecificAuthorizationHeader.bind( this, purpose - ) as IGetAuthorizationHeader; + ) as GetAuthorizationHeader; return getAuthorizationHeader; } } diff --git a/business/queryCache.ts b/business/queryCache.ts index 03c586097..d1610530d 100644 --- a/business/queryCache.ts +++ b/business/queryCache.ts @@ -16,15 +16,15 @@ import Debug from 'debug'; -import { MassagePermissionsToGitHubRepositoryPermission } from '../transitional'; -import { OrganizationMemberCacheEntity } from '../entities/organizationMemberCache/organizationMemberCache'; +import { projectCollaboratorPermissionToGitHubRepositoryPermission } from '../lib/transitional'; +import { OrganizationMemberCacheEntity } from './entities/organizationMemberCache/organizationMemberCache'; import { Operations } from './operations'; -import { TeamMemberCacheEntity } from '../entities/teamMemberCache/teamMemberCache'; +import { TeamMemberCacheEntity } from './entities/teamMemberCache/teamMemberCache'; -import { TeamCacheEntity } from '../entities/teamCache/teamCache'; -import { RepositoryTeamCacheEntity } from '../entities/repositoryTeamCache/repositoryTeamCache'; -import { RepositoryCacheEntity } from '../entities/repositoryCache/repositoryCache'; -import { RepositoryCollaboratorCacheEntity } from '../entities/repositoryCollaboratorCache/repositoryCollaboratorCache'; +import { TeamCacheEntity } from './entities/teamCache/teamCache'; +import { RepositoryTeamCacheEntity } from './entities/repositoryTeamCache/repositoryTeamCache'; +import { RepositoryCacheEntity } from './entities/repositoryCache/repositoryCache'; +import { RepositoryCollaboratorCacheEntity } from './entities/repositoryCollaboratorCache/repositoryCollaboratorCache'; import { Repository } from '.'; import { IProviders, @@ -665,9 +665,10 @@ export default class QueryCache { try { const organization = this.operations.getOrganizationById(Number(cacheEntity.organizationId)); const team = organization.team(Number(cacheEntity.teamId)); - const iid = cacheEntity.repositoryId; + const idAsStringOrNumber = cacheEntity.repositoryId; + const repositoryIdAsNumber = Number(idAsStringOrNumber); const repository = organization.repository(cacheEntity.repositoryName, { - id: cacheEntity.repositoryId, // a string version of repositoryId FYI + id: repositoryIdAsNumber, private: cacheEntity.repositoryPrivate, }); return { @@ -835,9 +836,8 @@ export default class QueryCache { this.throwMethodNotSupported('repositoryCollaborators', 'repositoryCollaboratorCacheProvider'); } const repositoryCollaboratorCacheProvider = this._providers.repositoryCollaboratorCacheProvider; - const rawEntities = await repositoryCollaboratorCacheProvider.queryCollaboratorsByRepositoryId( - repositoryId - ); + const rawEntities = + await repositoryCollaboratorCacheProvider.queryCollaboratorsByRepositoryId(repositoryId); return rawEntities .map((cacheEntity) => this.hydrateRepositoryCollaborator(cacheEntity)) .filter((real) => real); @@ -894,9 +894,10 @@ export default class QueryCache { cacheEntity: RepositoryCollaboratorCacheEntity ): IQueryCacheRepositoryCollaborator { const organization = this.operations.getOrganizationById(Number(cacheEntity.organizationId)); - const iid = cacheEntity.repositoryId; + const idAsStringOrNumber = cacheEntity.repositoryId; + const repositoryIdAsNumber = Number(idAsStringOrNumber); const repository = organization.repository(cacheEntity.repositoryName, { - id: cacheEntity.repositoryId, + id: repositoryIdAsNumber, private: cacheEntity.repositoryPrivate, }); // a string version of repositoryId FYI return { @@ -904,7 +905,7 @@ export default class QueryCache { affiliation: cacheEntity.collaboratorType, cacheEntity, userId: cacheEntity.userId, - permission: MassagePermissionsToGitHubRepositoryPermission(cacheEntity.permission), + permission: projectCollaboratorPermissionToGitHubRepositoryPermission(cacheEntity.permission), }; } @@ -993,9 +994,8 @@ export default class QueryCache { this.throwMethodNotSupported('organizationMembers', 'organizationMemberCacheProvider'); } const organizationMemberCacheProvider = this._providers.organizationMemberCacheProvider; - const rawEntities = await organizationMemberCacheProvider.queryOrganizationMembersByOrganizationId( - organizationId - ); + const rawEntities = + await organizationMemberCacheProvider.queryOrganizationMembersByOrganizationId(organizationId); return this.hydrateOrganizationMembers(rawEntities); } diff --git a/business/repoSearch.ts b/business/repoSearch.ts index 310f89ec8..945e54691 100644 --- a/business/repoSearch.ts +++ b/business/repoSearch.ts @@ -7,10 +7,10 @@ import querystring from 'querystring'; import { Repository } from './repository'; import { IPersonalizedUserAggregateRepositoryPermission } from './graphManager'; -import { RepositoryMetadataEntity } from '../entities/repositoryMetadata/repositoryMetadata'; -import { IRepositoryMetadataProvider } from '../entities/repositoryMetadata/repositoryMetadataProvider'; +import { RepositoryMetadataEntity } from './entities/repositoryMetadata/repositoryMetadata'; +import { IRepositoryMetadataProvider } from './entities/repositoryMetadata/repositoryMetadataProvider'; import { TeamRepositoryPermission } from './teamRepositoryPermission'; -import { sortRepositoriesByNameCaseInsensitive } from '../utils'; +import { sortRepositoriesByNameCaseInsensitive } from '../lib/utils'; import { GitHubRepositoryPermission, IRepositorySearchOptions, RepositoryLockdownState } from '../interfaces'; const defaultPageSize = 20; // GitHub.com seems to use a value around 33 diff --git a/business/repository.ts b/business/repository.ts index a1e82be00..f213ca64d 100644 --- a/business/repository.ts +++ b/business/repository.ts @@ -18,10 +18,10 @@ import { TeamMember, OrganizationMember, } from '.'; -import { RepositoryMetadataEntity } from '../entities/repositoryMetadata/repositoryMetadata'; -import { AppPurpose, AppPurposeTypes } from './githubApps'; +import { RepositoryMetadataEntity } from './entities/repositoryMetadata/repositoryMetadata'; +import { AppPurpose, AppPurposeTypes } from '../lib/github/appPurposes'; import { - IPurposefulGetAuthorizationHeader, + PurposefulGetAuthorizationHeader, IOperationsInstance, ICacheOptions, throwIfNotGitHubCapable, @@ -33,7 +33,7 @@ import { ITemporaryCommandOutput, NoCacheNoBackground, IGitHubProtectedBranchConfiguration, - IRepositoryBranchAccessProtections, + RepositoryBranchAccessProtections as RepositoryBranchAccessProtections, IListContributorsOptions, IGetCollaboratorsOptions, GitHubCollaboratorAffiliationQuery, @@ -44,19 +44,24 @@ import { IGitHubSecretScanningAlert, operationsWithCapability, IOperationsServiceAccounts, - IGetAuthorizationHeader, + GetAuthorizationHeader, IRepositoryGetIssuesOptions, IOperationsRepositoryMetadataProvider, IOperationsUrls, GitHubRepositoryPermission, + GitHubRepositoryVisibility, + GitHubRepositoryDetails, } from '../interfaces'; import { IListPullsParameters, GitHubPullRequestState } from '../lib/github/collections'; -import { wrapError } from '../utils'; +import { wrapError } from '../lib/utils'; import { RepositoryActions } from './repositoryActions'; import { RepositoryPullRequest } from './repositoryPullRequest'; -import { ErrorHelper } from '../transitional'; +import { CreateError, ErrorHelper } from '../lib/transitional'; import { augmentInertiaPreview, RepositoryProject } from './repositoryProject'; +import { RepositoryInvitation } from './repositoryInvitation'; +import { RepositoryProperties } from './repositoryProperties'; +import { WithGitHubRestHeaders } from '../lib/github/core'; interface IRepositoryMoments { created?: moment.Moment; @@ -160,6 +165,46 @@ interface IUnarchiveResponse { }; } +export type GitHubBranchProtectionParameters = { + owner: string; + repo: string; + branch: string; + required_status_checks: { + strict: boolean; + contexts: string[]; + checks?: string[]; + } | null; + enforce_admins: boolean | null; + required_pull_request_reviews: { + dismissal_restrictions: { + users: string[]; + teams: string[]; + apps: string[]; + }; + dismiss_stale_reviews: boolean; + require_code_owner_reviews: boolean; + required_approving_review_count: number; + require_last_push_approval: boolean; + bypass_pull_request_allowances: { + users: string[]; + teams: string[]; + apps: string[]; + }; + } | null; + restrictions: { + users: string[]; + teams: string[]; + apps: string[]; + }; + required_linear_history: boolean; + allow_force_pushes: boolean | null; + allow_deletions: boolean; + block_creations: boolean; + required_conversation_resolution: boolean; + lock_branch: boolean; + allow_fork_syncing: boolean; +}; + export type GitHubPagesResponse = { status: string; cname: string; @@ -203,6 +248,7 @@ const safeEntityFieldsForJsonSend = [ 'stargazers_count', 'open_issues_count', 'id', + 'visibility' ]; const sortByLogin = (list) => { @@ -218,7 +264,7 @@ const sortByLogin = (list) => { }; export class Repository { - private _entity: any; + private _entity: WithGitHubRestHeaders; private _baseUrl: string; private _absoluteBaseUrl: string; private _nativeUrl: string; @@ -226,17 +272,18 @@ export class Repository { private _awesomeness: number; - private _getAuthorizationHeader: IPurposefulGetAuthorizationHeader; - private _getSpecificAuthorizationHeader: IPurposefulGetAuthorizationHeader; + private _getAuthorizationHeader: PurposefulGetAuthorizationHeader; + private _getSpecificAuthorizationHeader: PurposefulGetAuthorizationHeader; private _operations: IOperationsInstance; private _organization: Organization; + private _customProperties: RepositoryProperties; private _name: string; private _moments: IRepositoryMoments; - getEntity(): any { + getEntity(): WithGitHubRestHeaders { return this._entity; } @@ -270,6 +317,9 @@ export class Repository { get private(): boolean { return this._entity ? this._entity.private : false; } + get visibility(): GitHubRepositoryVisibility { + return this._entity ? this._entity.visibility : null; + } get html_url(): string { return this._entity ? this._entity.html_url : null; } @@ -285,13 +335,13 @@ export class Repository { get archived(): boolean { return this._entity ? this._entity.archived : false; } - get created_at(): Date { + get created_at(): string { return this._entity ? this._entity.created_at : null; } - get updated_at(): Date { + get updated_at(): string { return this._entity ? this._entity.updated_at : null; } - get pushed_at(): Date { + get pushed_at(): string { return this._entity ? this._entity.pushed_at : null; } get git_url(): string { @@ -375,8 +425,8 @@ export class Repository { constructor( organization: Organization, entity: any, - getAuthorizationHeader: IPurposefulGetAuthorizationHeader, - getSpecificAuthorizationHeader: IPurposefulGetAuthorizationHeader, + getAuthorizationHeader: PurposefulGetAuthorizationHeader, + getSpecificAuthorizationHeader: PurposefulGetAuthorizationHeader, operations: IOperationsInstance ) { this._organization = organization; @@ -397,6 +447,17 @@ export class Repository { this._operations = operations; } + get customProperties() { + if (!this._customProperties) { + this._customProperties = new RepositoryProperties( + this, + this._operations, + this._getSpecificAuthorizationHeader.bind(this) + ); + } + return this._customProperties; + } + get moment(): IRepositoryMoments { if (!this._moments) { this._moments = { @@ -417,6 +478,17 @@ export class Repository { }; } + async getId(options?: ICacheOptions): Promise { + // Repositories by name may not actually have the ID; this ensures it's available + // and a number. Similar to previously checking "isDeleted" or "getDetails" first. + if (!this.id) { + await this.getDetails(options); + } + if (this.id) { + return typeof this.id === 'number' ? this.id : parseInt(this.id, 10); + } + } + async isDeleted(options?: ICacheOptions): Promise { try { await this.getDetails(options); @@ -437,12 +509,22 @@ export class Repository { ); } - async getDetails(options?: ICacheOptions): Promise { + async getDetails(options?: ICacheOptions): Promise> { options = options || {}; const operations = throwIfNotGitHubCapable(this._operations); - if (this.id && !this.name) { + const cacheOptions: ICacheOptions = { + maxAgeSeconds: getMaxAgeSeconds(operations, CacheDefault.orgRepoDetailsStaleSeconds, options), + }; + if (options.backgroundRefresh !== undefined) { + cacheOptions.backgroundRefresh = options.backgroundRefresh; + } + if ((options as any).noConditionalRequests === true) { + (cacheOptions as any).noConditionalRequests = true; + } + // always prefer ID over name + if (this.id) { try { - const lookupById = await this.organization.getRepositoryById(this.id); + const lookupById = await this.organization.getRepositoryById(this.id, cacheOptions); this._entity = lookupById.getEntity(); this._name = this._entity.name; } catch (getByIdError) { @@ -460,17 +542,8 @@ export class Repository { if (mediaType) { (parameters as any).mediaType = mediaType; } - const cacheOptions: ICacheOptions = { - maxAgeSeconds: getMaxAgeSeconds(operations, CacheDefault.orgRepoDetailsStaleSeconds, options), - }; - if (options.backgroundRefresh !== undefined) { - cacheOptions.backgroundRefresh = options.backgroundRefresh; - } - if ((options as any).noConditionalRequests === true) { - (cacheOptions as any).noConditionalRequests = true; - } try { - let entity: any = undefined; + let entity: WithGitHubRestHeaders = undefined; if ((cacheOptions as any)?.noConditionalRequests === true) { entity = await operations.github.post(this.authorize(AppPurpose.Data), 'repos.get', parameters); } else { @@ -758,6 +831,27 @@ export class Repository { } } + async updateBranchProtectionRule2( + parameters: GitHubBranchProtectionParameters, + cacheOptions?: ICacheOptions + ): Promise { + cacheOptions = cacheOptions || {}; + const operations = throwIfNotGitHubCapable(this._operations); + const github = operations.github; + + Object.assign(parameters, cacheOptions); + // PUT /repos/{owner}/{repo}/branches/{branch}/protection + const protections = await github.call( + this.authorize(AppPurpose.Data), + 'repos.updateBranchProtection', + parameters + ); + if (protections.length >= 100) { + console.warn('This API does not support pagination currently... there may be more items'); + } + return protections as RepositoryBranchAccessProtections; + } + async listBranchProtectionRules(): Promise { await this.organization.requireUpdatesApp('listBranchProtectionRules'); const query = `query($owner: String!, $repo: String!) { @@ -810,7 +904,7 @@ export class Repository { async getProtectedBranchAccessRestrictions( branchName: string, cacheOptions?: ICacheOptions - ): Promise { + ): Promise { // NOTE: GitHub has a "100-item limit" currently. This is an object response and not // technically paginated. cacheOptions = cacheOptions || {}; @@ -834,7 +928,31 @@ export class Repository { 'repos.getBranchProtection', parameters ); - return protections as IRepositoryBranchAccessProtections; + return protections as RepositoryBranchAccessProtections; + } + + async getAdminProtectedBranchAccessRestrictions( + branchName: string, + cacheOptions?: ICacheOptions + ): Promise { + // NOTE: GitHub has a "100-item limit" currently. This is an object response and not + // technically paginated. + cacheOptions = cacheOptions || {}; + const operations = throwIfNotGitHubCapable(this._operations); + const github = operations.github; + const parameters = { + owner: this.organization.name, + repo: this.name, + branch: branchName, + }; + Object.assign(parameters, cacheOptions); + // GET /repos/{owner}/{repo}/branches/{branch}/protection/enforce_admins + const protections = await github.call( + this.authorize(AppPurpose.Data), + 'repos.getAdminBranchProtection', + parameters + ); + return protections as RepositoryBranchAccessProtections; } async setDefaultBranch(defaultBranchName: string): Promise { @@ -882,9 +1000,8 @@ export class Repository { cacheOptions.backgroundRefresh = options.backgroundRefresh; } try { - // CONSIDER: need a fallback authentication approach: try and app for a specific capability (the installation knows) and fallback to central ops - // const centralOps = operationsWithCapability(operations, CoreCapability.GitHubCentralOperations); - const tokenSource = this._getSpecificAuthorizationHeader(AppPurpose.Data); // centralOps ? centralOps.getCentralOperationsToken()(AppPurpose.Data) : + // CONSIDER: need a fallback authentication approach: try and app for a specific capability + const tokenSource = this._getSpecificAuthorizationHeader(AppPurpose.Data); const token = await tokenSource; return await operations.github.call(token, 'repos.getPages', parameters, cacheOptions); } catch (error) { @@ -1056,6 +1173,39 @@ export class Repository { return collaborators; } + async listCollaboratorInvitations(cacheOptions?: IPagedCacheOptions): Promise { + cacheOptions = cacheOptions || {}; + const operations = throwIfNotGitHubCapable(this._operations); + const github = operations.github; + const parameters = { + owner: this.organization.name, + repo: this.name, + per_page: getPageSize(operations), + }; + if (!cacheOptions.maxAgeSeconds) { + cacheOptions.maxAgeSeconds = getMaxAgeSeconds( + operations, + CacheDefault.orgRepoCollaboratorsStaleSeconds + ); + } + if (cacheOptions.backgroundRefresh === undefined) { + cacheOptions.backgroundRefresh = true; + } + const invitationEntities = await github.collections.getRepoInvitations( + this.authorize(AppPurpose.Data), + parameters, + cacheOptions + ); + const invitations = common.createInstances( + this, + invitationFromEntity, + invitationEntities + ); + invitationEntities?.cost && ((invitations as any).cost = invitationEntities.cost); + invitationEntities?.headers && ((invitations as any).headers = invitationEntities.headers); + return invitations; + } + async addCollaborator( username: string, permission: GitHubRepositoryPermission @@ -1330,9 +1480,6 @@ export class Repository { private: options.private, } ); - // BUG: GitHub Apps do not work with locking down no repository permissions as documented here: https://github.community/t5/GitHub-API-Development-and/GitHub-App-cannot-patch-repo-visibility-in-org-with-repo/m-p/33448#M3150 - // const token = this._operations.getCentralOperationsToken(); - // return this._operations.github.post(token, 'repos.update', parameters); return operations.github.post(this.authorize(AppPurpose.Operations), 'repos.update', parameters); } @@ -1554,7 +1701,10 @@ export class Repository { const teams = (await this.getTeamPermissions()).filter((tp) => tp.permission === 'admin'); for (let i = 0; i < teams.length; i++) { const team = teams[i]; - if (excludeBroadAndSystemTeams && (team.team.isSystemTeam || team.team.isBroadAccessTeam)) { + if ( + excludeBroadAndSystemTeams && + (team.team.isSystemTeam || team.team.isBroadAccessTeam || team.team.isOpenAccessTeam) + ) { // Do not include broad access teams continue; } @@ -1734,19 +1884,16 @@ export class Repository { return Array.from(users.values()); } - private authorize(purpose: AppPurposeTypes): IGetAuthorizationHeader | string { - const getAuthorizationHeader = this._getAuthorizationHeader.bind( - this, - purpose - ) as IGetAuthorizationHeader; + private authorize(purpose: AppPurposeTypes): GetAuthorizationHeader | string { + const getAuthorizationHeader = this._getAuthorizationHeader.bind(this, purpose) as GetAuthorizationHeader; return getAuthorizationHeader; } - private specificAuthorization(purpose: AppPurposeTypes): IGetAuthorizationHeader | string { + private specificAuthorization(purpose: AppPurposeTypes): GetAuthorizationHeader | string { const getSpecificHeader = this._getSpecificAuthorizationHeader.bind( this, purpose - ) as IGetAuthorizationHeader; + ) as GetAuthorizationHeader; return getSpecificHeader; } @@ -2014,3 +2161,9 @@ function collaboratorPermissionFromEntity(entity) { const permission = new Collaborator(entity); return permission; } + +function invitationFromEntity(entity) { + // 'this' is bound for this function to be a private method + const invitation = new RepositoryInvitation(this, entity); + return invitation; +} diff --git a/business/repositoryActions.ts b/business/repositoryActions.ts index ec2d2b539..fff60e0c7 100644 --- a/business/repositoryActions.ts +++ b/business/repositoryActions.ts @@ -5,16 +5,13 @@ import { Repository } from './repository'; import { getPageSize, getMaxAgeSeconds, CacheDefault } from '.'; -import { AppPurpose } from './githubApps'; +import { AppPurpose } from '../lib/github/appPurposes'; import { - IPurposefulGetAuthorizationHeader, + PurposefulGetAuthorizationHeader, IOperationsInstance, - IGetBranchesOptions, - IGitHubBranch, throwIfNotGitHubCapable, - IGetPullsOptions, ICacheOptions, - IGetAuthorizationHeader, + GetAuthorizationHeader, } from '../interfaces'; export interface IGitHubActionWorkflowsResponse { @@ -36,16 +33,16 @@ export interface IGitHubActionWorkflow { } export class RepositoryActions { - private _getAuthorizationHeader: IPurposefulGetAuthorizationHeader; - private _getSpecificAuthorizationHeader: IPurposefulGetAuthorizationHeader; + private _getAuthorizationHeader: PurposefulGetAuthorizationHeader; + private _getSpecificAuthorizationHeader: PurposefulGetAuthorizationHeader; private _operations: IOperationsInstance; private _repository: Repository; constructor( repository: Repository, - getAuthorizationHeader: IPurposefulGetAuthorizationHeader, - getSpecificAuthorizationHeader: IPurposefulGetAuthorizationHeader, + getAuthorizationHeader: PurposefulGetAuthorizationHeader, + getSpecificAuthorizationHeader: PurposefulGetAuthorizationHeader, operations: IOperationsInstance ) { this._repository = repository; @@ -178,11 +175,11 @@ export class RepositoryActions { return entity; } - private authorize(purpose: AppPurpose): IGetAuthorizationHeader | string { + private authorize(purpose: AppPurpose): GetAuthorizationHeader | string { const getAuthorizationHeader = this._getSpecificAuthorizationHeader.bind( this, purpose - ) as IGetAuthorizationHeader; + ) as GetAuthorizationHeader; return getAuthorizationHeader; } } diff --git a/business/repositoryInvitation.ts b/business/repositoryInvitation.ts new file mode 100644 index 000000000..449774cbb --- /dev/null +++ b/business/repositoryInvitation.ts @@ -0,0 +1,77 @@ +// +// Copyright (c) Microsoft. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +import { GitHubRepositoryPermission, GitHubSimpleAccount } from '../interfaces'; +import * as common from './common'; +import { Repository } from './repository'; + +const primaryProperties = ['inviter', 'invitee', 'permissions', 'created_at', 'html_url', 'node_id']; + +export type RepositoryInvitationClientJson = { + inviter: { + id: number; + login: string; + }; + invitee: { + id: number; + login: string; + }; + permissions: GitHubRepositoryPermission; + created_at: string; + html_url: string; + // node_id: string; +}; + +export class RepositoryInvitation { + public static PrimaryProperties = primaryProperties; + + private _inviter: GitHubSimpleAccount; + private _invitee: GitHubSimpleAccount; + private _permissions: GitHubRepositoryPermission; + private _html_url: string; + private _created_at: string; + + constructor( + private repository: Repository, + entity: unknown + ) { + if (entity) { + common.assignKnownFieldsPrefixed(this, entity, 'invitation', primaryProperties); + } + } + + asJson(): RepositoryInvitationClientJson { + return { + invitee: this.invitee, + inviter: this.inviter, + permissions: this.permission, + html_url: this.invitationUrl, + created_at: this._created_at, + }; + } + + get permission(): GitHubRepositoryPermission { + return this._permissions; + } + + // getHighestPermission() { + // if (!this._permissions) { + // return GitHubRepositoryPermission.None; + // } + // return projectCollaboratorPermissionsObjectToGitHubRepositoryPermission(this._permissions); + // } + + get inviter(): GitHubSimpleAccount { + return this._inviter; + } + + get invitee(): GitHubSimpleAccount { + return this._invitee; + } + + get invitationUrl(): string { + return this._html_url; + } +} diff --git a/business/repositoryIssue.ts b/business/repositoryIssue.ts index 936bc606b..3e80750e0 100644 --- a/business/repositoryIssue.ts +++ b/business/repositoryIssue.ts @@ -4,25 +4,25 @@ // import { Repository } from './repository'; -import { wrapError } from '../utils'; -import { AppPurpose } from './githubApps'; +import { wrapError } from '../lib/utils'; +import { AppPurpose } from '../lib/github/appPurposes'; import { CacheDefault, getMaxAgeSeconds, Operations } from '.'; import { IOperationsInstance, - IPurposefulGetAuthorizationHeader, + PurposefulGetAuthorizationHeader, GitHubIssueState, IIssueLabel, throwIfNotGitHubCapable, ICacheOptions, - IGetAuthorizationHeader, + GetAuthorizationHeader, GitHubIssuePatchParameters, GitHubStateReason, } from '../interfaces'; -import { CreateError, ErrorHelper } from '../transitional'; +import { CreateError, ErrorHelper } from '../lib/transitional'; export class RepositoryIssue { private _operations: IOperationsInstance; - private _getAuthorizationHeader: IPurposefulGetAuthorizationHeader; + private _getAuthorizationHeader: PurposefulGetAuthorizationHeader; private _number: number; private _repository: Repository; @@ -33,7 +33,7 @@ export class RepositoryIssue { repository: Repository, issueNumber: number, operations: IOperationsInstance, - getAuthorizationHeader: IPurposefulGetAuthorizationHeader, + getAuthorizationHeader: PurposefulGetAuthorizationHeader, entity?: any ) { this._getAuthorizationHeader = getAuthorizationHeader; @@ -211,11 +211,8 @@ export class RepositoryIssue { return false; } - private authorize(purpose: AppPurpose): IGetAuthorizationHeader | string { - const getAuthorizationHeader = this._getAuthorizationHeader.bind( - this, - purpose - ) as IGetAuthorizationHeader; + private authorize(purpose: AppPurpose): GetAuthorizationHeader | string { + const getAuthorizationHeader = this._getAuthorizationHeader.bind(this, purpose) as GetAuthorizationHeader; return getAuthorizationHeader; } diff --git a/business/repositoryPermission.ts b/business/repositoryPermission.ts index daea8021f..a0dbcca24 100644 --- a/business/repositoryPermission.ts +++ b/business/repositoryPermission.ts @@ -9,17 +9,21 @@ import { GitHubCollaboratorPermissionLevel, ConvertGitHubCollaboratorPermissionLevelToGitHubRepositoryPermission, GitHubRepositoryPermission, + IGitHubCollaboratorPermissions, } from '../interfaces'; +import type { CollaboratorAccount, CollaboratorJson } from './collaborator'; // prettier-ignore const repoPermissionProperties = [ 'permission', 'user', + 'role_name', ]; export class RepositoryPermission { - private _id: string; - private _user: any; + private _id: number; + private _user: CollaboratorAccount; + private _role_name: string; private _permission: GitHubCollaboratorPermissionLevel; @@ -32,17 +36,86 @@ export class RepositoryPermission { } } - get id(): string { + get id(): number { return this._id; } + + get roleName(): string { + return this._role_name; + } + get permission(): GitHubCollaboratorPermissionLevel { return this._permission; } - get user(): any { + + get user(): CollaboratorAccount { return this._user; } - public asGitHubRepositoryPermission(): GitHubRepositoryPermission { + asCollaboratorJson(): CollaboratorJson { + return { + avatar_url: null, + id: this._id, + login: this._user?.login, + permissions: this.asCollaboratorPermissions(), + }; + } + + asCollaboratorPermissions(): IGitHubCollaboratorPermissions { + return repositoryPermissionToPermissionsObject(this.asGitHubLegacyRepositoryPermission()); + } + + asGitHubLegacyRepositoryPermission(): GitHubRepositoryPermission { + // GitHub's API will only return "admin", "read", "write"; while the function + // implements recognition of maintain, etc., it isn't a thing. return ConvertGitHubCollaboratorPermissionLevelToGitHubRepositoryPermission(this._permission); } + + hasCustomRolePermission() { + switch (this._role_name) { + case GitHubRepositoryPermission.Admin: + case GitHubRepositoryPermission.Maintain: + case GitHubRepositoryPermission.Triage: + case GitHubRepositoryPermission.Push: + case GitHubRepositoryPermission.Pull: + return false; + default: + return true; + } + } + + interpretRoleAsDetailedPermission(): GitHubRepositoryPermission { + if (!this.hasCustomRolePermission()) { + return this._role_name as GitHubRepositoryPermission; + } + return this.asGitHubLegacyRepositoryPermission(); + } +} + +export function repositoryPermissionToPermissionsObject( + permission: GitHubRepositoryPermission +): IGitHubCollaboratorPermissions { + const permissions: IGitHubCollaboratorPermissions = { + admin: false, + maintain: false, + push: false, + triage: false, + pull: false, + }; + if (permission === GitHubRepositoryPermission.Admin) { + permissions.admin = true; + } + if (permission === GitHubRepositoryPermission.Maintain || permissions.admin === true) { + permissions.maintain = true; + } + if (permission === GitHubRepositoryPermission.Push || permissions.maintain === true) { + permissions.push = true; + } + if (permission === GitHubRepositoryPermission.Triage || permissions.push === true) { + permissions.triage = true; + } + if (permission === GitHubRepositoryPermission.Pull || permissions.triage === true) { + permissions.pull = true; + } + return permissions; } diff --git a/business/repositoryProject.ts b/business/repositoryProject.ts index 5bf5d084e..034eea1d9 100644 --- a/business/repositoryProject.ts +++ b/business/repositoryProject.ts @@ -4,26 +4,26 @@ // import { Repository } from './repository'; -import { wrapError } from '../utils'; -import { AppPurpose, AppPurposeTypes } from './githubApps'; +import { wrapError } from '../lib/utils'; +import { AppPurpose, AppPurposeTypes } from '../lib/github/appPurposes'; import { CacheDefault, getMaxAgeSeconds } from '.'; import { IOperationsInstance, - IPurposefulGetAuthorizationHeader, + PurposefulGetAuthorizationHeader, GitHubIssueState, throwIfNotGitHubCapable, ICacheOptions, - IGetAuthorizationHeader, + GetAuthorizationHeader, ICacheOptionsWithPurpose, } from '../interfaces'; -import { ErrorHelper } from '../transitional'; +import { ErrorHelper } from '../lib/transitional'; import { RepositoryProjectColumn } from './repositoryProjectColumn'; import * as common from './common'; export class RepositoryProject { private _operations: IOperationsInstance; - private _getAuthorizationHeader: IPurposefulGetAuthorizationHeader; - private _getSpecificAuthorizationHeader: IPurposefulGetAuthorizationHeader; + private _getAuthorizationHeader: PurposefulGetAuthorizationHeader; + private _getSpecificAuthorizationHeader: PurposefulGetAuthorizationHeader; private _id: number; private _repository: Repository; @@ -36,8 +36,8 @@ export class RepositoryProject { repository: Repository, projectId: number, operations: IOperationsInstance, - getAuthorizationHeader: IPurposefulGetAuthorizationHeader, - getSpecificAuthorizationHeader: IPurposefulGetAuthorizationHeader, + getAuthorizationHeader: PurposefulGetAuthorizationHeader, + getSpecificAuthorizationHeader: PurposefulGetAuthorizationHeader, entity?: any ) { this._getAuthorizationHeader = getAuthorizationHeader; @@ -213,11 +213,11 @@ export class RepositoryProject { return false; } - private authorizeSpecificPurpose(purpose: AppPurposeTypes): IGetAuthorizationHeader | string { + private authorizeSpecificPurpose(purpose: AppPurposeTypes): GetAuthorizationHeader | string { const getAuthorizationHeader = this._getSpecificAuthorizationHeader.bind( this, purpose - ) as IGetAuthorizationHeader; + ) as GetAuthorizationHeader; return getAuthorizationHeader; } } diff --git a/business/repositoryProjectCard.ts b/business/repositoryProjectCard.ts index 596c3a14d..2e052422c 100644 --- a/business/repositoryProjectCard.ts +++ b/business/repositoryProjectCard.ts @@ -3,17 +3,13 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { AppPurpose } from './githubApps'; -import { - IOperationsInstance, - IPurposefulGetAuthorizationHeader, - IGetAuthorizationHeader, -} from '../interfaces'; +import { AppPurpose } from '../lib/github/appPurposes'; +import { IOperationsInstance, PurposefulGetAuthorizationHeader, GetAuthorizationHeader } from '../interfaces'; import { RepositoryProjectColumn } from './repositoryProjectColumn'; export class RepositoryProjectCard { private _operations: IOperationsInstance; - private _getAuthorizationHeader: IPurposefulGetAuthorizationHeader; + private _getAuthorizationHeader: PurposefulGetAuthorizationHeader; private _id: number; private _column: RepositoryProjectColumn; @@ -24,7 +20,7 @@ export class RepositoryProjectCard { column: RepositoryProjectColumn, cardId: number, operations: IOperationsInstance, - getAuthorizationHeader: IPurposefulGetAuthorizationHeader, + getAuthorizationHeader: PurposefulGetAuthorizationHeader, entity?: any ) { this._getAuthorizationHeader = getAuthorizationHeader; @@ -64,11 +60,8 @@ export class RepositoryProjectCard { return this._column; } - private authorize(purpose: AppPurpose): IGetAuthorizationHeader | string { - const getAuthorizationHeader = this._getAuthorizationHeader.bind( - this, - purpose - ) as IGetAuthorizationHeader; + private authorize(purpose: AppPurpose): GetAuthorizationHeader | string { + const getAuthorizationHeader = this._getAuthorizationHeader.bind(this, purpose) as GetAuthorizationHeader; return getAuthorizationHeader; } diff --git a/business/repositoryProjectColumn.ts b/business/repositoryProjectColumn.ts index c69fc7017..d0dd408d2 100644 --- a/business/repositoryProjectColumn.ts +++ b/business/repositoryProjectColumn.ts @@ -4,11 +4,11 @@ // import { CacheDefault, getMaxAgeSeconds, RepositoryIssue } from '.'; -import { AppPurpose, AppPurposeTypes } from './githubApps'; +import { AppPurpose, AppPurposeTypes } from '../lib/github/appPurposes'; import { IOperationsInstance, - IPurposefulGetAuthorizationHeader, - IGetAuthorizationHeader, + PurposefulGetAuthorizationHeader, + GetAuthorizationHeader, ICacheOptionsWithPurpose, throwIfNotGitHubCapable, ICacheOptions, @@ -16,11 +16,11 @@ import { import { augmentInertiaPreview, RepositoryProject } from './repositoryProject'; import { RepositoryProjectCard } from './repositoryProjectCard'; import * as common from './common'; -import { CreateError } from '../transitional'; +import { CreateError } from '../lib/transitional'; export class RepositoryProjectColumn { private _operations: IOperationsInstance; - private _getSpecificAuthorizationHeader: IPurposefulGetAuthorizationHeader; + private _getSpecificAuthorizationHeader: PurposefulGetAuthorizationHeader; private _id: number; private _project: RepositoryProject; @@ -31,7 +31,7 @@ export class RepositoryProjectColumn { project: RepositoryProject, columnId: number, operations: IOperationsInstance, - getSpecificAuthorizationHeader: IPurposefulGetAuthorizationHeader, + getSpecificAuthorizationHeader: PurposefulGetAuthorizationHeader, entity?: any ) { this._getSpecificAuthorizationHeader = getSpecificAuthorizationHeader; @@ -136,11 +136,11 @@ export class RepositoryProjectColumn { return card; } - private authorizeSpecificPurpose(purpose: AppPurposeTypes): IGetAuthorizationHeader | string { + private authorizeSpecificPurpose(purpose: AppPurposeTypes): GetAuthorizationHeader | string { const getAuthorizationHeader = this._getSpecificAuthorizationHeader.bind( this, purpose - ) as IGetAuthorizationHeader; + ) as GetAuthorizationHeader; return getAuthorizationHeader; } } diff --git a/business/repositoryProperties.ts b/business/repositoryProperties.ts new file mode 100644 index 000000000..f653ebff6 --- /dev/null +++ b/business/repositoryProperties.ts @@ -0,0 +1,77 @@ +// +// Copyright (c) Microsoft. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +import { Repository } from './repository'; +import { AppPurpose, AppPurposeTypes } from '../lib/github/appPurposes'; +import { createCacheOptions, popPurpose, symbolizeApiResponse } from '.'; +import { + IOperationsInstance, + PurposefulGetAuthorizationHeader, + throwIfNotGitHubCapable, + GetAuthorizationHeader, + ICacheOptionsWithPurpose, +} from '../interfaces'; +import { OrganizationCustomPropertySetPropertyValue } from './organizationProperties'; + +export class RepositoryProperties { + private _defaultPurpose = AppPurpose.Data; + private _getSpecificAuthorizationHeader: PurposefulGetAuthorizationHeader; + + constructor( + public readonly repository: Repository, + private operations: IOperationsInstance, + getSpecificAuthorizationHeader: PurposefulGetAuthorizationHeader + ) { + this._getSpecificAuthorizationHeader = getSpecificAuthorizationHeader; + } + + createOrUpdateProperties( + propertiesAndValues: Record, + purpose?: AppPurposeTypes + ): Promise { + const names = [this.repository.name]; + const organizationProperties = this.repository.organization.customProperties; + return organizationProperties.createOrUpdateRepositoriesProperties(names, propertiesAndValues, purpose); + } + + async getProperties(options?: ICacheOptionsWithPurpose): Promise> { + options = options || {}; + const operations = throwIfNotGitHubCapable(this.operations); + const { github } = operations; + const purpose = popPurpose(options, this._defaultPurpose); + const parameters = { + owner: this.repository.organization.name, + repo: this.repository.name, + }; + const cacheOptions = createCacheOptions(operations, options); + try { + const responseArray = await github.request( + this.authorize(purpose), + 'GET /repos/:owner/:repo/properties/values', + parameters, + cacheOptions + ); + return symbolizeApiResponse(arrayToSetProperties(responseArray)); + } catch (error) { + throw error; + } + } + + private authorize(purpose: AppPurposeTypes): GetAuthorizationHeader | string { + const getAuthorizationHeader = this._getSpecificAuthorizationHeader.bind( + this, + purpose + ) as GetAuthorizationHeader; + return getAuthorizationHeader; + } +} + +function arrayToSetProperties(properties: OrganizationCustomPropertySetPropertyValue[]) { + const propertiesAndValues: Record = {}; + for (const property of properties) { + propertiesAndValues[property.property_name] = property.value; + } + return propertiesAndValues; +} diff --git a/business/repositoryPullRequest.ts b/business/repositoryPullRequest.ts index 81d2b8a51..41fad1c01 100644 --- a/business/repositoryPullRequest.ts +++ b/business/repositoryPullRequest.ts @@ -4,26 +4,26 @@ // import { Repository } from './repository'; -import { wrapError } from '../utils'; -import { AppPurpose } from './githubApps'; +import { wrapError } from '../lib/utils'; +import { AppPurpose } from '../lib/github/appPurposes'; import { CacheDefault, getMaxAgeSeconds } from '.'; import { IOperationsInstance, - IPurposefulGetAuthorizationHeader, + PurposefulGetAuthorizationHeader, GitHubIssueState, IIssueLabel, throwIfNotGitHubCapable, ICacheOptions, - IGetAuthorizationHeader, + GetAuthorizationHeader, } from '../interfaces'; -import { ErrorHelper } from '../transitional'; +import { ErrorHelper } from '../lib/transitional'; // Pull requests are issues but not all issues are pull requests. So this is mostly a clone of repositoryIssue.ts // right now, with slightly different endpoints. export class RepositoryPullRequest { private _operations: IOperationsInstance; - private _getAuthorizationHeader: IPurposefulGetAuthorizationHeader; + private _getAuthorizationHeader: PurposefulGetAuthorizationHeader; private _number: number; private _repository: Repository; @@ -34,7 +34,7 @@ export class RepositoryPullRequest { repository: Repository, pullRequestNumber: number, operations: IOperationsInstance, - getAuthorizationHeader: IPurposefulGetAuthorizationHeader, + getAuthorizationHeader: PurposefulGetAuthorizationHeader, entity?: any ) { this._getAuthorizationHeader = getAuthorizationHeader; @@ -219,11 +219,8 @@ export class RepositoryPullRequest { return false; } - private authorize(purpose: AppPurpose): IGetAuthorizationHeader | string { - const getAuthorizationHeader = this._getAuthorizationHeader.bind( - this, - purpose - ) as IGetAuthorizationHeader; + private authorize(purpose: AppPurpose): GetAuthorizationHeader | string { + const getAuthorizationHeader = this._getAuthorizationHeader.bind(this, purpose) as GetAuthorizationHeader; return getAuthorizationHeader; } } diff --git a/business/team.ts b/business/team.ts index d2950b1e6..33e17002f 100644 --- a/business/team.ts +++ b/business/team.ts @@ -8,16 +8,16 @@ import _ from 'lodash'; import * as common from './common'; -import { wrapError } from '../utils'; +import { wrapError } from '../lib/utils'; import { TeamMember } from './teamMember'; import { TeamRepositoryPermission } from './teamRepositoryPermission'; -import { IApprovalProvider } from '../entities/teamJoinApproval/approvalProvider'; -import { TeamJoinApprovalEntity } from '../entities/teamJoinApproval/teamJoinApproval'; -import { AppPurpose } from './githubApps'; +import { IApprovalProvider } from './entities/teamJoinApproval/approvalProvider'; +import { TeamJoinApprovalEntity } from './entities/teamJoinApproval/teamJoinApproval'; +import { AppPurpose } from '../lib/github/appPurposes'; import { CacheDefault, getMaxAgeSeconds, getPageSize, Organization } from '.'; import { IOperationsInstance, - IPurposefulGetAuthorizationHeader, + PurposefulGetAuthorizationHeader, TeamJsonFormat, throwIfNotCapable, IOperationsUrls, @@ -25,7 +25,7 @@ import { ICacheOptions, throwIfNotGitHubCapable, IPagedCacheOptions, - IGetAuthorizationHeader, + GetAuthorizationHeader, IUpdateTeamMembershipOptions, GitHubTeamRole, ITeamMembershipRoleState, @@ -36,8 +36,9 @@ import { IGetTeamRepositoriesOptions, GitHubRepositoryType, IOperationsProviders, + GitHubTeamDetails, } from '../interfaces'; -import { validateGitHubLogin, ErrorHelper } from '../transitional'; +import { validateGitHubLogin, ErrorHelper } from '../lib/transitional'; const teamPrimaryProperties = [ 'id', @@ -82,7 +83,7 @@ export class Team { private _organization: Organization; private _operations: IOperationsInstance; - private _getAuthorizationHeader: IPurposefulGetAuthorizationHeader; + private _getAuthorizationHeader: PurposefulGetAuthorizationHeader; private _id: number; @@ -139,7 +140,7 @@ export class Team { constructor( organization: Organization, entity, - getAuthorizationHeader: IPurposefulGetAuthorizationHeader, + getAuthorizationHeader: PurposefulGetAuthorizationHeader, operations: IOperationsInstance ) { if (!entity || !entity.id) { @@ -181,6 +182,7 @@ export class Team { clone.corporateMetadata = { isSystemTeam: this.isSystemTeam, isBroadAccessTeam: this.isBroadAccessTeam, + isOpenAccessTeam: this.isOpenAccessTeam, }; return clone; } @@ -216,7 +218,7 @@ export class Team { if (this._name && this._slug) { return; } - return await this.getDetails(); + await this.getDetails(); } async isDeleted(options?: ICacheOptions): Promise { @@ -230,7 +232,7 @@ export class Team { return false; } - async getDetails(options?: ICacheOptions): Promise { + async getDetails(options?: ICacheOptions): Promise { options = options || {}; const operations = throwIfNotGitHubCapable(this._operations); const cacheOptions = { @@ -303,7 +305,7 @@ export class Team { const getAuthorizationHeader = this._getAuthorizationHeader.bind( this, AppPurpose.Data - ) as IGetAuthorizationHeader; + ) as GetAuthorizationHeader; const teamEntities = await github.collections.getTeamChildTeams( getAuthorizationHeader, parameters, @@ -323,6 +325,15 @@ export class Team { return res >= 0; } + get isOpenAccessTeam(): boolean { + const teams = this._organization.openAccessTeams; + if (typeof this._id !== 'number') { + throw new Error('Team.id must be a number'); + } + const res = teams.indexOf(this._id); + return res >= 0; + } + get isSystemTeam(): boolean { const systemTeams = this._organization.systemTeamIds; const res = systemTeams.indexOf(this._id); @@ -687,11 +698,8 @@ export class Team { }; } - private authorize(purpose: AppPurpose): IGetAuthorizationHeader | string { - const getAuthorizationHeader = this._getAuthorizationHeader.bind( - this, - purpose - ) as IGetAuthorizationHeader; + private authorize(purpose: AppPurpose): GetAuthorizationHeader | string { + const getAuthorizationHeader = this._getAuthorizationHeader.bind(this, purpose) as GetAuthorizationHeader; return getAuthorizationHeader; } } diff --git a/business/teamMember.ts b/business/teamMember.ts index bca38e787..7e68c535f 100644 --- a/business/teamMember.ts +++ b/business/teamMember.ts @@ -23,6 +23,12 @@ const memberPrimaryProperties = [ ]; const memberSecondaryProperties = []; +export type TeamMemberJson = { + id: number; + login: string; + avatar_url: string; +}; + export class TeamMember { private _team: Team; private _operations: IOperationsInstance; @@ -76,7 +82,7 @@ export class TeamMember { this._operations = operations; } - asJson() { + asJson(): TeamMemberJson { return { id: this.id, login: this.login, diff --git a/business/teamPermission.ts b/business/teamPermission.ts index f05f49d28..3fd781410 100644 --- a/business/teamPermission.ts +++ b/business/teamPermission.ts @@ -9,24 +9,61 @@ import { Organization } from './organization'; import { TeamMember } from './teamMember'; import { Team } from '.'; import { - IOperationsInstance, + type IOperationsInstance, GitHubTeamPrivacy, TeamJsonFormat, - IGetMembersOptions, + type IGetMembersOptions, GitHubRepositoryPermission, + type IGitHubTeamBasics, } from '../interfaces'; +import { projectCollaboratorPermissionsObjectToGitHubRepositoryPermission } from '../lib/transitional'; + +export interface ITeamRepositoryPermission { + pull: boolean; + triage: boolean; + push: boolean; + maintain: boolean; + admin: boolean; +} + +export function isStandardGitHubTeamPermission(val: string | GitHubRepositoryPermission) { + switch (val) { + case GitHubRepositoryPermission.Pull: + case GitHubRepositoryPermission.Triage: + case GitHubRepositoryPermission.Push: + case GitHubRepositoryPermission.Maintain: + case GitHubRepositoryPermission.Admin: + return true; + default: + return false; + } +} + +type TeamPermissionIncomingEntity = { + name: string; + id: number; + // node_id: we remove this currently + slug: string; + description: string; + privacy: GitHubTeamPrivacy; + permission: GitHubRepositoryPermission | string; + permissions: ITeamRepositoryPermission; + parent: IGitHubTeamBasics; +}; export class TeamPermission { + // private _operations: IOperationsInstance; private _organization: Organization; - private _operations: IOperationsInstance; private _team: Team; - private _permission: GitHubRepositoryPermission; + private _permission: GitHubRepositoryPermission | string; private _privacy: GitHubTeamPrivacy; private _teamMembersIfSet: TeamMember[]; + private _entity: TeamPermissionIncomingEntity; + [util.inspect.custom](depth, options) { return `GitHub Team Permission: team=${this.team?.slug || this.team?.id} permission=${this._permission}`; } @@ -41,7 +78,7 @@ export class TeamPermission { }; } - get permission(): GitHubRepositoryPermission { + get permission(): string | GitHubRepositoryPermission { return this._permission; } @@ -53,8 +90,13 @@ export class TeamPermission { return this._team; } - constructor(organization: Organization, entity: any, operations: IOperationsInstance) { + constructor( + organization: Organization, + entity: TeamPermissionIncomingEntity, + operations: IOperationsInstance + ) { this._organization = organization; + this._entity = entity; this._permission = entity.permission; this._privacy = entity.privacy; @@ -65,7 +107,7 @@ export class TeamPermission { const id = entity.id; this._team = organization.team(id, entity); - this._operations = operations; + // this._operations = operations; } get relativeJoinLink() { @@ -82,6 +124,20 @@ export class TeamPermission { } } + get customRoleName() { + if (!isStandardGitHubTeamPermission(this._entity.permission)) { + return this._entity.permission; + } + } + + get permissions(): ITeamRepositoryPermission { + return this._entity.permissions; + } + + getAsPermission(): GitHubRepositoryPermission { + return projectCollaboratorPermissionsObjectToGitHubRepositoryPermission(this._entity.permissions); + } + async resolveTeamMembers(options?: IGetMembersOptions): Promise { this._teamMembersIfSet = await this.team.getMembers(options); } diff --git a/business/teamRepositoryPermission.ts b/business/teamRepositoryPermission.ts index 1ed650604..972d8e51d 100644 --- a/business/teamRepositoryPermission.ts +++ b/business/teamRepositoryPermission.ts @@ -5,29 +5,48 @@ import { Team } from './team'; import { Repository } from './repository'; -import { IOperationsInstance } from '../interfaces'; + +import type { GitHubRepositoryPermission, IOperationsInstance } from '../interfaces'; +import { isStandardGitHubTeamPermission, type ITeamRepositoryPermission } from './teamPermission'; +import { projectCollaboratorPermissionsObjectToGitHubRepositoryPermission } from '../lib/transitional'; + +// this is used when a team returns the repositories it can work with; +// the GitHub API is pretty inconsistent. The actual entities are a combination of +// repository AND permission here. + +type RepositoryWithTeamPermissionsEntity = { + id: number; + name: string; + full_name: string; + private: boolean; + description: string; + fork: boolean; + permissions: ITeamRepositoryPermission; + role_name: string; +}; export class TeamRepositoryPermission { + // private _operations: IOperationsInstance; + + private _entity: RepositoryWithTeamPermissionsEntity; + private _team: Team; - private _operations: IOperationsInstance; - private _permissions: any; private _repository: Repository; private _id: number; - constructor(team: Team, entity: any, operations: IOperationsInstance) { + constructor(team: Team, entity: RepositoryWithTeamPermissionsEntity, operations: IOperationsInstance) { this._team = team; if (!entity) { throw new Error('TeamRepositoryPermission: requires entity'); } - this._permissions = entity.permissions; + this._entity = entity; this._repository = team.organization.repositoryFromEntity(entity); this._id = this._repository.id; - this._operations = operations; } asJson() { const repo = this._repository.asJson(); - const permissions = this._permissions; + const permissions = this.permissions; const combined = { ...repo, permissions }; return combined; } @@ -44,8 +63,18 @@ export class TeamRepositoryPermission { return this._id; } - get permissions(): any { - return this._permissions; + get customRoleName() { + if (!isStandardGitHubTeamPermission(this._entity.role_name)) { + return this._entity.role_name; + } + } + + get permissions(): ITeamRepositoryPermission { + return this._entity.permissions; + } + + getAsPermission(): GitHubRepositoryPermission { + return projectCollaboratorPermissionsObjectToGitHubRepositoryPermission(this._entity.permissions); } get name(): string { diff --git a/business/user/aggregate.ts b/business/user/aggregate.ts index 30aabe83f..63116e9e5 100644 --- a/business/user/aggregate.ts +++ b/business/user/aggregate.ts @@ -19,7 +19,7 @@ import { OrganizationMembershipRoleQuery, GitHubRepositoryPermission, } from '../../interfaces'; -import { SettleToStateValue, isPermissionBetterThan, ErrorHelper } from '../../transitional'; +import { SettleToStateValue, isPermissionBetterThan, ErrorHelper } from '../../lib/transitional'; import LinkManager from './linkManager'; // PLANNING once consolidated @@ -267,7 +267,11 @@ export class UserContext { const permission = tp.permission; const entity = { ...team.toSimpleJsonObject() }; entity['permission'] = permission; - const teamRepositoryPermission = new TeamRepositoryPermission(team, entity, this._operations); + const teamRepositoryPermission = new TeamRepositoryPermission( + team, + entity as any /* RepositoryWithTeamPermissionsEntity */, + this._operations + ); if (isPermissionBetterThan(bestPermission, permission)) { bestPermission = permission; } diff --git a/business/user/index.ts b/business/user/index.ts index 93d52895e..e74c64fdd 100644 --- a/business/user/index.ts +++ b/business/user/index.ts @@ -10,7 +10,7 @@ import objectPath from 'object-path'; import Debug from 'debug'; const debug = Debug.debug('context'); -import { addBreadcrumb, isCodespacesAuthenticating } from '../../utils'; +import { addBreadcrumb, isCodespacesAuthenticating } from '../../lib/utils'; import { Operations } from '../operations'; import { UserContext } from './aggregate'; import { @@ -537,7 +537,7 @@ export class IndividualContext { async isPortalAdministrator(): Promise { const operations = this._operations; - const ghi = this.getGitHubIdentity().username; + const ghi = this.getGitHubIdentity()?.username; const link = this._link; this._isPortalAdministrator = await operations.isPortalSudoer(ghi, link); return this._isPortalAdministrator; diff --git a/webhooks/organizationProcessor.ts b/business/webhooks/organizationProcessor.ts similarity index 88% rename from webhooks/organizationProcessor.ts rename to business/webhooks/organizationProcessor.ts index a84150a9a..d20905548 100644 --- a/webhooks/organizationProcessor.ts +++ b/business/webhooks/organizationProcessor.ts @@ -6,18 +6,21 @@ import crypto from 'crypto'; import secureCompare from 'secure-compare'; -import { Operations } from '../business'; -import { Organization } from '../business'; +import { Organization } from '..'; -import Tasks from './tasks'; -import { sleep } from '../utils'; -import { type IProviders } from '../interfaces'; +import { sleep } from '../../lib/utils'; +import { type IProviders } from '../../interfaces'; + +import defaultWebhookTasks from './tasks'; +import getCompanySpecificDeployment from '../../middleware/companySpecificDeployment'; interface IValidationError extends Error { statusCode?: number; computedHash?: string; } +let companySpecificWebhookTasks: WebhookProcessor[] = null; + export abstract class WebhookProcessor { abstract filter(data: any): boolean; abstract run(providers: IProviders, organization: Organization, data: any): Promise; @@ -50,6 +53,14 @@ export default async function ProcessOrganizationWebhook( if (!providers) { throw new Error('No providers provided'); } + const companySpecific = getCompanySpecificDeployment(); + if ( + companySpecific?.features?.firehose?.getAdditionalWebhookTasks && + companySpecificWebhookTasks === null + ) { + companySpecificWebhookTasks = + await companySpecific.features.firehose.getAdditionalWebhookTasks(providers); + } const organization = options.organization; const event = options.event; if (!organization || !organization.name) { @@ -110,7 +121,8 @@ export default async function ProcessOrganizationWebhook( options.acknowledgeValidEvent(); } let interestingEvents = 0; - const work = Tasks.filter((task) => task.filter(event)); + const availableTasks = [...defaultWebhookTasks, ...(companySpecificWebhookTasks || [])]; + const work = availableTasks.filter((task) => task.filter(event)); if (work.length > 0) { ++interestingEvents; console.log(`[* interesting event: ${event.properties.event} (${work.length} interested tasks)]`); diff --git a/webhooks/tasks/auditLog.ts b/business/webhooks/tasks/auditLog.ts similarity index 97% rename from webhooks/tasks/auditLog.ts rename to business/webhooks/tasks/auditLog.ts index c8c7d37e1..8aff9f335 100644 --- a/webhooks/tasks/auditLog.ts +++ b/business/webhooks/tasks/auditLog.ts @@ -9,10 +9,10 @@ // for organizations, and also to import JSON-based audit export files. import { WebhookProcessor } from '../organizationProcessor'; -import { Organization } from '../../business'; +import { Organization } from '../..'; import { AuditLogRecord } from '../../entities/auditLogRecord/auditLogRecord'; import { MapWebhookEventsToAuditEvents, AuditLogSource } from '../../entities/auditLogRecord'; -import type { IProviders } from '../../interfaces'; +import type { IProviders } from '../../../interfaces'; // prettier-ignore const eventTypes = new Set([ @@ -28,6 +28,7 @@ const knownEventTypesToIgnore = new Set([ 'fork', 'watch', 'star', + 'installation_repositories', ]); async function runAsync(providers: IProviders, organization: Organization, data: any) { diff --git a/webhooks/tasks/automaticTeams.ts b/business/webhooks/tasks/automaticTeams.ts similarity index 95% rename from webhooks/tasks/automaticTeams.ts rename to business/webhooks/tasks/automaticTeams.ts index 0f93e3416..76df95476 100644 --- a/webhooks/tasks/automaticTeams.ts +++ b/business/webhooks/tasks/automaticTeams.ts @@ -7,13 +7,13 @@ const teamTypes = ['read', 'write', 'admin']; const defaultLargeAdminTeamSize = 250; import { WebhookProcessor } from '../organizationProcessor'; -import { Operations } from '../../business'; -import { Organization } from '../../business'; +import { Operations } from '../..'; +import { Organization } from '../..'; -import RenderHtmlMail from '../../lib/emailRender'; -import { IMailProvider } from '../../lib/mailProvider'; -import getCompanySpecificDeployment from '../../middleware/companySpecificDeployment'; -import { GitHubRepositoryPermission, IProviders } from '../../interfaces'; +import RenderHtmlMail from '../../../lib/emailRender'; +import { IMailProvider } from '../../../lib/mailProvider'; +import getCompanySpecificDeployment from '../../../middleware/companySpecificDeployment'; +import { GitHubRepositoryPermission, IProviders } from '../../../interfaces'; interface IAutomaticTeamsMail { to: string; @@ -31,7 +31,7 @@ interface ICustomDataEventName { export default class AutomaticTeamsWebhookProcessor implements WebhookProcessor { processOrgSpecialTeams(organization: Organization) { - const specialTeams = organization.specialRepositoryPermissionTeams; + const specialTeams = organization.specialSystemTeams; const specials = []; const specialTeamIds = new Set(); const specialTeamLevels = new Map(); @@ -362,9 +362,8 @@ async function setTeamPermission( const orgName = organization.name; const repository = organization.repository(repoName, { id: repoId }); if (customizedTeamPermissionsWebhookLogic) { - const shouldSkipEnforcement = await customizedTeamPermissionsWebhookLogic.shouldSkipEnforcement( - repository - ); + const shouldSkipEnforcement = + await customizedTeamPermissionsWebhookLogic.shouldSkipEnforcement(repository); if (shouldSkipEnforcement && necessaryPermission !== GitHubRepositoryPermission.Pull) { console.log( `Customized logic for team permissions: skipping enforcement for repository ${repository.id}` diff --git a/webhooks/tasks/index.ts b/business/webhooks/tasks/index.ts similarity index 100% rename from webhooks/tasks/index.ts rename to business/webhooks/tasks/index.ts diff --git a/webhooks/tasks/member.ts b/business/webhooks/tasks/member.ts similarity index 92% rename from webhooks/tasks/member.ts rename to business/webhooks/tasks/member.ts index 17941bab8..f5c49f79b 100644 --- a/webhooks/tasks/member.ts +++ b/business/webhooks/tasks/member.ts @@ -6,9 +6,9 @@ // COLLABORATOR on a repository import { WebhookProcessor } from '../organizationProcessor'; -import { Operations, Organization } from '../../business'; -import { IProviders, GitHubCollaboratorType } from '../../interfaces'; -import { ErrorHelper } from '../../transitional'; +import { Operations, Organization } from '../..'; +import { IProviders, GitHubCollaboratorType } from '../../../interfaces'; +import { ErrorHelper } from '../../../lib/transitional'; export default class MemberWebhookProcessor implements WebhookProcessor { filter(data: any) { @@ -68,8 +68,7 @@ export default class MemberWebhookProcessor implements WebhookProcessor { const repositoryName = event.repository.name; const repository = organization.repository(repositoryName, event.repository); const collaborator = await repository.getCollaborator(event.member.login); - const permission = collaborator.asGitHubRepositoryPermission(); - // TODO: may need to support the new 5 levels vs 3... + const permission = collaborator.interpretRoleAsDetailedPermission(); if (permission) { const isOrganizationMember = await organization.getMembership(userLogin); const collaboratorType = isOrganizationMember diff --git a/webhooks/tasks/membership.ts b/business/webhooks/tasks/membership.ts similarity index 96% rename from webhooks/tasks/membership.ts rename to business/webhooks/tasks/membership.ts index 730bb3645..6a1dca5a2 100644 --- a/webhooks/tasks/membership.ts +++ b/business/webhooks/tasks/membership.ts @@ -4,8 +4,8 @@ // import { WebhookProcessor } from '../organizationProcessor'; -import { Operations, Organization } from '../../business'; -import { IProviders, GitHubTeamRole } from '../../interfaces'; +import { Operations, Organization } from '../..'; +import { IProviders, GitHubTeamRole } from '../../../interfaces'; export default class MembershipWebhookProcessor implements WebhookProcessor { filter(data: any) { diff --git a/webhooks/tasks/organization.ts b/business/webhooks/tasks/organization.ts similarity index 98% rename from webhooks/tasks/organization.ts rename to business/webhooks/tasks/organization.ts index b2a49039f..03b9e2d4d 100644 --- a/webhooks/tasks/organization.ts +++ b/business/webhooks/tasks/organization.ts @@ -5,13 +5,13 @@ // ORGANIZATION membership and ownership -import { Organization } from '../../business'; +import { Organization } from '../..'; import { OrganizationMembershipRole, type IProviders, NoCacheNoBackground, OrganizationMembershipState, -} from '../../interfaces'; +} from '../../../interfaces'; import { WebhookProcessor } from '../organizationProcessor'; // NOTE: unfortunately role changes from admin->member or member->admin do not fire GitHub hooks diff --git a/webhooks/tasks/repository.ts b/business/webhooks/tasks/repository.ts similarity index 93% rename from webhooks/tasks/repository.ts rename to business/webhooks/tasks/repository.ts index eda134063..76a31f571 100644 --- a/webhooks/tasks/repository.ts +++ b/business/webhooks/tasks/repository.ts @@ -6,9 +6,9 @@ // REPOSITORY created or updated import { WebhookProcessor } from '../organizationProcessor'; -import { Organization } from '../../business'; +import { Organization } from '../..'; import NewRepositoryLockdownSystem from '../../features/newRepositories/newRepositoryLockdown'; -import { getRepositoryMetadataProvider, RepositoryLockdownState, type IProviders } from '../../interfaces'; +import { getRepositoryMetadataProvider, RepositoryLockdownState, type IProviders } from '../../../interfaces'; export default class RepositoryWebhookProcessor implements WebhookProcessor { filter(data: any) { @@ -17,7 +17,7 @@ export default class RepositoryWebhookProcessor implements WebhookProcessor { } async run(providers: IProviders, organization: Organization, data: any): Promise { - const { immutable, operations } = providers; + const { immutable, insights, operations } = providers; const event = data.body; const queryCache = operations.providers.queryCache; let update = false; @@ -26,6 +26,9 @@ export default class RepositoryWebhookProcessor implements WebhookProcessor { let transferSourceLogin: string = null; const action = event.action; const organizationId = event.organization.id as number; + const repositoryId = event?.repository?.id as number; + const repositoryIdAsString = String(repositoryId); + const organizationIdAsString = String(organizationId); if (!operations.isOrganizationManagedById(organizationId)) { console.log( `skipping organization ID ${organizationId} which is not directly managed: ${event.organization.login}` @@ -57,8 +60,6 @@ export default class RepositoryWebhookProcessor implements WebhookProcessor { } by ${event.sender.login}` ); update = true; - const repositoryIdAsString = event.repository.id.toString(); - const organizationIdAsString = event.organization.id.toString(); try { if ( organizationIdAsString === organization.id.toString() && @@ -88,8 +89,6 @@ export default class RepositoryWebhookProcessor implements WebhookProcessor { console.log(`repository event not being intercepted: ${action}`); } if (addOrUpdateRepositoryQueryCache) { - const repositoryIdAsString = event.repository.id.toString(); - const organizationIdAsString = event.organization.id.toString(); try { if ( organizationIdAsString === organization.id.toString() && @@ -132,6 +131,7 @@ export default class RepositoryWebhookProcessor implements WebhookProcessor { const repository = organization.repository(event.repository.name, event.repository); const repositoryMetadataProvider = getRepositoryMetadataProvider(organization.operations); const lockdownSystem = new NewRepositoryLockdownSystem({ + insights, operations, organization, repository, diff --git a/webhooks/tasks/team.ts b/business/webhooks/tasks/team.ts similarity index 89% rename from webhooks/tasks/team.ts rename to business/webhooks/tasks/team.ts index 7f9aedcdb..bdbfd249f 100644 --- a/webhooks/tasks/team.ts +++ b/business/webhooks/tasks/team.ts @@ -5,20 +5,15 @@ // TEAM changes -import moment from 'moment'; - import { WebhookProcessor } from '../organizationProcessor'; -import { Operations } from '../../business'; -import { Organization } from '../../business'; -import { permissionsObjectToValue } from '../../transitional'; -import type { IProviders } from '../../interfaces'; +import { Operations } from '../..'; +import { Organization } from '../..'; +import { projectCollaboratorPermissionsObjectToGitHubRepositoryPermission } from '../../../lib/transitional'; +import type { IProviders } from '../../../interfaces'; // When teams are added or removed on GitHub, refresh the organization's list of // teams as well as the cross-organization view of the teams. -// TODO: connect to query cache -// TODO: consider whether to slowly kick off Redis cache updates, too - export default class TeamWebhookProcessor implements WebhookProcessor { filter(data: any) { const eventType = data.properties.event; @@ -32,7 +27,7 @@ export default class TeamWebhookProcessor implements WebhookProcessor { let refresh = false; let expectedAfterRefresh = false; const teamId = event.team.id; - const teamIdAsString = event.team.id.toString(); + const teamIdAsString = String(teamId); const organizationIdAsString = event.organization.id.toString(); let addOrUpdate = false; if (event.action === 'created') { @@ -64,10 +59,12 @@ export default class TeamWebhookProcessor implements WebhookProcessor { queryCache && queryCache.supportsTeamPermissions ) { - const oldRepositoryPermissionLevel = permissionsObjectToValue( + const oldRepositoryPermissionLevel = projectCollaboratorPermissionsObjectToGitHubRepositoryPermission( event.changes.repository.permissions.from ); - const newRepositoryPermissionLevel = permissionsObjectToValue(event.repository.permissions); + const newRepositoryPermissionLevel = projectCollaboratorPermissionsObjectToGitHubRepositoryPermission( + event.repository.permissions + ); console.log( `team ${event.team.name} permission level for repo ${event.repository.name} changed from ${oldRepositoryPermissionLevel} to ${newRepositoryPermissionLevel}` ); @@ -100,7 +97,7 @@ export default class TeamWebhookProcessor implements WebhookProcessor { isPrivate, repoName, event.team.id.toString(), - permissionsObjectToValue(event.repository.permissions) + projectCollaboratorPermissionsObjectToGitHubRepositoryPermission(event.repository.permissions) ); // equiv to event.team.permission as GitHubRepositoryPermission } } @@ -131,7 +128,6 @@ export default class TeamWebhookProcessor implements WebhookProcessor { } if (refresh) { - const startingRefresh = moment(); // organization.getTeams(immediateRefreshOptions, () => { // console.log('refreshing teams list after add or remove operations'); // const now = moment(); diff --git a/config/brand.types.ts b/config/brand.types.ts index 4c16aa981..62c89a816 100644 --- a/config/brand.types.ts +++ b/config/brand.types.ts @@ -17,5 +17,4 @@ export type ConfigBrand = { operationsMail: string; forkApprovalMail: string; electionMail: string; - infrastructureNotificationsMail: string; }; diff --git a/config/continuousDeployment.types.ts b/config/continuousDeployment.types.ts index 4c7d22560..52602db41 100644 --- a/config/continuousDeployment.types.ts +++ b/config/continuousDeployment.types.ts @@ -8,6 +8,9 @@ export type ConfigRootContinuousDeployment = { }; export type ConfigContinuousDeployment = { + branchName: string; + build: string; + commitId: string; version: string; name: string; }; diff --git a/config/debug.json b/config/debug.json index 56175a688..1f904f311 100644 --- a/config/debug.json +++ b/config/debug.json @@ -2,5 +2,7 @@ "environmentName": "env://ENVIRONMENT_NAME?default=Unknown", "showUsers": "env://SITE_SHOW_USERS?trueIf=show", "showDebugFooter": "env://DEBUG_SHOW_FOOTER?trueIf=1", - "unlinkWithoutDrops": "env://DEBUG_UNLINK_WITHOUT_DROPS?trueIf=1" + "exitImmediately": "env://EXIT_IMMEDIATELY?trueIf=1", + "unlinkWithoutDrops": "env://DEBUG_UNLINK_WITHOUT_DROPS?trueIf=1", + "breakConsoleEveryMinute": "env://DEBUG_MINUTE_BREAKS?trueIf=1" } diff --git a/config/debug.types.ts b/config/debug.types.ts index 4c6c7b2a9..2dc85bd95 100644 --- a/config/debug.types.ts +++ b/config/debug.types.ts @@ -12,4 +12,6 @@ export type ConfigDebug = { showUsers: boolean; showDebugFooter: boolean; unlinkWithoutDrops: boolean; + exitImmediately: boolean; + breakConsoleEveryMinute?: boolean; }; diff --git a/config/github.operations.json b/config/github.operations.json index 73b95dee3..22222b72d 100644 --- a/config/github.operations.json +++ b/config/github.operations.json @@ -1,5 +1,4 @@ { - "centralOperationsToken": "env://GITHUB_CENTRAL_OPERATIONS_TOKEN", "publicAccessToken": "env://GITHUB_PUBLIC_OPERATIONS_TOKEN", "primaryOrganizationId": "env://GITHUB_PRIMARY_ORGANIZATION_ID?type=integer" } diff --git a/config/github.organizations.types.ts b/config/github.organizations.types.ts index b33c37ada..3e0ba49a1 100644 --- a/config/github.organizations.types.ts +++ b/config/github.organizations.types.ts @@ -19,6 +19,7 @@ export type ConfigGitHubOrganization = { teamAllReposRead: string; // | number teamAllReposWrite: string; // | number teamAllReposAdmin: string; + teamOpenAccess: string; teamSudoers: string; templates: string[]; onboarding: boolean; diff --git a/config/npm.publishing.json b/config/npm.publishing.json deleted file mode 100644 index a6b728dac..000000000 --- a/config/npm.publishing.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "former_token": "env://NPM_PUBLISHING_TOKEN", - "token": "", - "notify": "env://NPM_PUBLISHING_NOTIFICATION_EMAIL", - "notifyFrom": "env://NPM_PUBLISHING_NOTIFICATION_FROM" -} diff --git a/config/npm.publishing.types.ts b/config/npm.publishing.types.ts deleted file mode 100644 index 6eccf0230..000000000 --- a/config/npm.publishing.types.ts +++ /dev/null @@ -1,17 +0,0 @@ -// -// Copyright (c) Microsoft. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. -// - -export type ConfigNpmRootPublishing = { - publishing: ConfigNpmPublishing; -}; - -// NOTE: this config and associated concept is no longer part of this application - -export type ConfigNpmPublishing = { - former_token: string; - token: string; - notify: string; - notifyFrom: string; -}; diff --git a/config/npm.types.ts b/config/npm.types.ts index 1299e552a..e3e943afa 100644 --- a/config/npm.types.ts +++ b/config/npm.types.ts @@ -3,12 +3,10 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import type { ConfigNpmRootPublishing } from './npm.publishing.types'; - export type ConfigRootNpm = { npm: ConfigNpm; }; -export type ConfigNpm = ConfigNpmRootPublishing & { +export type ConfigNpm = { privateFeedScope: string; }; diff --git a/config/webServer.json b/config/webServer.json index 5ad274329..f5f9bf26f 100644 --- a/config/webServer.json +++ b/config/webServer.json @@ -4,7 +4,18 @@ "appService": { "slot": "env://WEBSITE_SLOT_NAME", "name": "env://WEBSITE_SITE_NAME", - "region": "env://REGION_NAME" + "region": "env://REGION_NAME", + "advanced": { + "resourceGroup": "env://WEBSITE_RESOURCE_GROUP", + "warmup": "env://WEBSITE_WARMUP_PATH", + "swapWarmup": "env://WEBSITE_SWAP_WARMUP_PING_PATH", + "containerName": "env://WEBSITES_WEB_CONTAINER_NAME", + "instanceId": "env://WEBSITE_INSTANCE_ID", + "sku": "env://WEBSITE_SKU", + "hostname": "env://WEBSITE_HOSTNAME", + "alwaysOn": "env://WEBSITE_SCM_ALWAYS_ON_ENABLED", + "slotType": "env://SITE_SLOT_TYPE" + } }, "baseUrl": "env://SITE_BASE_URL", "sslify": { diff --git a/config/webServer.types.ts b/config/webServer.types.ts index b531fe90b..c716230d7 100644 --- a/config/webServer.types.ts +++ b/config/webServer.types.ts @@ -14,6 +14,17 @@ export type ConfigWebServer = { slot: string; name: string; region: string; + advanced?: { + resourceGroup: string; + warmup: string; + swapWarmup: string; + containerName: string; + instanceId: string; + sku: string; + hostname: string; + alwaysOn: string; + slotType: 'production' | 'staging' | undefined; + }; }; baseUrl: string; sslify: { diff --git a/pg.sql b/data/pg.sql similarity index 100% rename from pg.sql rename to data/pg.sql diff --git a/default-assets-package/package-lock.json b/default-assets-package/package-lock.json index 35d2a3b52..1306cdac6 100644 --- a/default-assets-package/package-lock.json +++ b/default-assets-package/package-lock.json @@ -36,23 +36,6 @@ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true }, - "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, - "optional": 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": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", @@ -149,79 +132,18 @@ "node": ">=0.10.0" } }, - "node_modules/asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=", - "dev": true, - "optional": true - }, - "node_modules/asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "dev": true, - "optional": true, - "dependencies": { - "safer-buffer": "~2.1.0" - } - }, - "node_modules/assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true, - "optional": true, - "engines": { - "node": ">=0.8" - } - }, "node_modules/async": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz", "integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==", "dev": true }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true, - "optional": true - }, - "node_modules/aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true, - "optional": true, - "engines": { - "node": "*" - } - }, - "node_modules/aws4": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", - "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", - "dev": true, - "optional": true - }, "node_modules/balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true }, - "node_modules/bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "dev": true, - "optional": true, - "dependencies": { - "tweetnacl": "^0.14.3" - } - }, "node_modules/blockui": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/blockui/-/blockui-1.0.0.tgz", @@ -276,13 +198,6 @@ "d3": "~3.5.0" } }, - "node_modules/caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true, - "optional": true - }, "node_modules/chalk": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", @@ -299,15 +214,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/clone": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", - "dev": true, - "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", @@ -335,19 +241,6 @@ "node": ">=0.1.90" } }, - "node_modules/combined-stream": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", - "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", - "dev": true, - "optional": true, - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/components-font-awesome": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/components-font-awesome/-/components-font-awesome-4.2.0.tgz", @@ -359,12 +252,17 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, - "node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "node_modules/copy-anything": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz", + "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==", "dev": true, - "optional": true + "dependencies": { + "is-what": "^3.14.1" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } }, "node_modules/d3": { "version": "3.5.17", @@ -372,19 +270,6 @@ "integrity": "sha1-vEZ0gAQ3iyGjYMn8fPUjF5B2L7g=", "dev": true }, - "node_modules/dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "dev": true, - "optional": true, - "dependencies": { - "assert-plus": "^1.0.0" - }, - "engines": { - "node": ">=0.10" - } - }, "node_modules/dateformat": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", @@ -394,16 +279,6 @@ "node": "*" } }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true, - "optional": true, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/detect-file": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", @@ -418,17 +293,6 @@ "resolved": "https://registry.npmjs.org/dom/-/dom-0.0.2.tgz", "integrity": "sha1-4V+3WV4ym9Enj8yFjQ97jPOOS1E=" }, - "node_modules/ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "dev": true, - "optional": true, - "dependencies": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, "node_modules/errno": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", @@ -497,30 +361,6 @@ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "dev": true }, - "node_modules/extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "dev": true, - "engines": [ - "node >=0.6.0" - ], - "optional": true - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "optional": true - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", - "dev": true, - "optional": true - }, "node_modules/file-sync-cmp": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/file-sync-cmp/-/file-sync-cmp-0.1.1.tgz", @@ -613,31 +453,6 @@ "node": ">=0.10.0" } }, - "node_modules/forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true, - "optional": true, - "engines": { - "node": "*" - } - }, - "node_modules/form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, - "optional": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 0.12" - } - }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -659,16 +474,6 @@ "node": ">=10" } }, - "node_modules/getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, - "optional": true, - "dependencies": { - "assert-plus": "^1.0.0" - } - }, "node_modules/glob": { "version": "7.1.7", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", @@ -1076,31 +881,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true, - "optional": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/har-validator": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", - "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", - "deprecated": "this library is no longer supported", - "dev": true, - "optional": true, - "dependencies": { - "ajv": "^6.5.5", - "har-schema": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -1155,22 +935,6 @@ "node": "*" } }, - "node_modules/http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dev": true, - "optional": true, - "dependencies": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - }, - "engines": { - "node": ">=0.8", - "npm": ">=1.3.7" - } - }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -1303,13 +1067,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true, - "optional": true - }, "node_modules/is-unc-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", @@ -1322,6 +1079,12 @@ "node": ">=0.10.0" } }, + "node_modules/is-what": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz", + "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==", + "dev": true + }, "node_modules/is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", @@ -1346,13 +1109,6 @@ "node": ">=0.10.0" } }, - "node_modules/isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "dev": true, - "optional": true - }, "node_modules/jquery": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.5.0.tgz", @@ -1371,50 +1127,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true, - "optional": true - }, - "node_modules/json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", - "dev": true, - "optional": true - }, - "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, - "optional": 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": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true, - "optional": true - }, - "node_modules/jsprim": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", - "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", - "dev": true, - "optional": true, - "dependencies": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" - }, - "engines": { - "node": ">=0.6.0" - } - }, "node_modules/kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -1425,27 +1137,27 @@ } }, "node_modules/less": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/less/-/less-3.9.0.tgz", - "integrity": "sha512-31CmtPEZraNUtuUREYjSqRkeETFdyEHSEPAGq4erDlUXtda7pzNmctdljdIagSb589d/qXGWiiP31R5JVf+v0w==", + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/less/-/less-3.13.1.tgz", + "integrity": "sha512-SwA1aQXGUvp+P5XdZslUOhhLnClSLIjWvJhmd+Vgib5BFIr9lMNlQwmwUNOjXThF/A0x+MCYYPeWEfeWiLRnTw==", "dev": true, "dependencies": { - "clone": "^2.1.2" + "copy-anything": "^2.0.1", + "tslib": "^1.10.0" }, "bin": { "lessc": "bin/lessc" }, "engines": { - "node": ">=4" + "node": ">=6" }, "optionalDependencies": { "errno": "^0.1.1", "graceful-fs": "^4.1.2", "image-size": "~0.5.0", + "make-dir": "^2.1.0", "mime": "^1.4.1", - "mkdirp": "^0.5.0", - "promise": "^7.1.1", - "request": "^2.83.0", + "native-request": "^1.0.5", "source-map": "~0.6.0" } }, @@ -1539,6 +1251,20 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, + "node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "optional": true, + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/make-iterator": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", @@ -1586,29 +1312,6 @@ "node": ">=4" } }, - "node_modules/mime-db": { - "version": "1.38.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.38.0.tgz", - "integrity": "sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg==", - "dev": true, - "optional": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.22", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.22.tgz", - "integrity": "sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog==", - "dev": true, - "optional": true, - "dependencies": { - "mime-db": "~1.38.0" - }, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/minimatch": { "version": "3.0.8", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz", @@ -1621,26 +1324,6 @@ "node": "*" } }, - "node_modules/minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", - "dev": true, - "optional": true - }, - "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==", - "dev": true, - "optional": true, - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, "node_modules/multimatch": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-2.1.0.tgz", @@ -1656,6 +1339,13 @@ "node": ">=0.10.0" } }, + "node_modules/native-request": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/native-request/-/native-request-1.1.0.tgz", + "integrity": "sha512-uZ5rQaeRn15XmpgE0xoPL8YWqcX90VtCFglYwAgkvKM5e8fog+vePLAhHxuuv/gRkrQxIeh5U3q9sMNUrENqWw==", + "dev": true, + "optional": true + }, "node_modules/nopt": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", @@ -1668,16 +1358,6 @@ "nopt": "bin/nopt.js" } }, - "node_modules/oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "dev": true, - "optional": true, - "engines": { - "node": "*" - } - }, "node_modules/object.defaults": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", @@ -1853,13 +1533,6 @@ "node": ">=0.10.0" } }, - "node_modules/performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true, - "optional": true - }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -1872,6 +1545,16 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "optional": true, + "engines": { + "node": ">=6" + } + }, "node_modules/pkg-up": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz", @@ -1896,16 +1579,6 @@ "node": ">=4" } }, - "node_modules/promise": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", - "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", - "dev": true, - "optional": true, - "dependencies": { - "asap": "~2.0.3" - } - }, "node_modules/prr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", @@ -1913,33 +1586,6 @@ "dev": true, "optional": true }, - "node_modules/psl": { - "version": "1.1.31", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz", - "integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==", - "dev": true, - "optional": true - }, - "node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true, - "optional": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/qs": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", - "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", - "dev": true, - "optional": true, - "engines": { - "node": ">=0.6" - } - }, "node_modules/rechoir": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", @@ -1952,39 +1598,6 @@ "node": ">= 0.10" } }, - "node_modules/request": { - "version": "2.88.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", - "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", - "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", - "dev": true, - "optional": true, - "dependencies": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.0", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.4.3", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "engines": { - "node": ">= 4" - } - }, "node_modules/resolve": { "version": "1.22.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", @@ -2048,19 +1661,22 @@ "rimraf": "bin.js" } }, - "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==", - "dev": true, - "optional": true - }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, + "node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "optional": true, + "bin": { + "semver": "bin/semver" + } + }, "node_modules/source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -2076,32 +1692,6 @@ "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", "dev": true }, - "node_modules/sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "dev": true, - "optional": true, - "dependencies": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - }, - "bin": { - "sshpk-conv": "bin/sshpk-conv", - "sshpk-sign": "bin/sshpk-sign", - "sshpk-verify": "bin/sshpk-verify" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", @@ -2159,46 +1749,11 @@ "node": ">=8.0" } }, - "node_modules/tough-cookie": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", - "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", - "dev": true, - "optional": true, - "dependencies": { - "psl": "^1.1.24", - "punycode": "^1.4.1" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/tough-cookie/node_modules/punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true, - "optional": true - }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, - "optional": true, - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, - "node_modules/tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "dev": true, - "optional": true + "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/typeahead": { "version": "0.2.2", @@ -2239,33 +1794,12 @@ "node": "*" } }, - "node_modules/uri-js": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", - "dev": true, - "optional": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true }, - "node_modules/uuid": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", - "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", - "dev": true, - "optional": true, - "bin": { - "uuid": "bin/uuid" - } - }, "node_modules/v8flags": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.2.0.tgz", @@ -2278,21 +1812,6 @@ "node": ">= 0.10" } }, - "node_modules/verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "dev": true, - "engines": [ - "node >=0.6.0" - ], - "optional": true, - "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, "node_modules/which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", @@ -2327,19 +1846,6 @@ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "optional": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", @@ -2411,73 +1917,18 @@ "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", "dev": true }, - "asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=", - "dev": true, - "optional": true - }, - "asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "dev": true, - "optional": true, - "requires": { - "safer-buffer": "~2.1.0" - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true, - "optional": true - }, "async": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz", "integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==", "dev": true }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true, - "optional": true - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true, - "optional": true - }, - "aws4": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", - "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", - "dev": true, - "optional": true - }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "dev": true, - "optional": true, - "requires": { - "tweetnacl": "^0.14.3" - } - }, "blockui": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/blockui/-/blockui-1.0.0.tgz", @@ -2523,13 +1974,6 @@ "d3": "~3.5.0" } }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true, - "optional": true - }, "chalk": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", @@ -2540,12 +1984,6 @@ "supports-color": "^7.1.0" } }, - "clone": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=", - "dev": true - }, "color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -2567,16 +2005,6 @@ "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", "dev": true }, - "combined-stream": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", - "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", - "dev": true, - "optional": true, - "requires": { - "delayed-stream": "~1.0.0" - } - }, "components-font-awesome": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/components-font-awesome/-/components-font-awesome-4.2.0.tgz", @@ -2588,12 +2016,14 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "copy-anything": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz", + "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==", "dev": true, - "optional": true + "requires": { + "is-what": "^3.14.1" + } }, "d3": { "version": "3.5.17", @@ -2601,29 +2031,12 @@ "integrity": "sha1-vEZ0gAQ3iyGjYMn8fPUjF5B2L7g=", "dev": true }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "dev": true, - "optional": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, "dateformat": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", "dev": true }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true, - "optional": true - }, "detect-file": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", @@ -2635,17 +2048,6 @@ "resolved": "https://registry.npmjs.org/dom/-/dom-0.0.2.tgz", "integrity": "sha1-4V+3WV4ym9Enj8yFjQ97jPOOS1E=" }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "dev": true, - "optional": true, - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, "errno": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", @@ -2695,27 +2097,6 @@ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "dev": true }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "dev": true, - "optional": true - }, - "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, - "optional": true - }, - "fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", - "dev": true, - "optional": true - }, "file-sync-cmp": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/file-sync-cmp/-/file-sync-cmp-0.1.1.tgz", @@ -2789,25 +2170,6 @@ "for-in": "^1.0.1" } }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true, - "optional": true - }, - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, - "optional": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -2826,16 +2188,6 @@ "integrity": "sha512-tj18lLe+917AACr6BdVoUuHnBPTVd9BEJp1vxnMZ58ztNvuxz9Ufa+wf3g37tlGITH35jggwZ2d9lcgHJJgXfQ==", "dev": true }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, - "optional": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, "glob": { "version": "7.1.7", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", @@ -3150,24 +2502,6 @@ } } }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true, - "optional": true - }, - "har-validator": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", - "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", - "dev": true, - "optional": true, - "requires": { - "ajv": "^6.5.5", - "har-schema": "^2.0.0" - } - }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -3207,18 +2541,6 @@ "integrity": "sha1-uDT3I8xKJCqmWWNFnfbZhMXT2Vk=", "dev": true }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dev": true, - "optional": true, - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -3321,13 +2643,6 @@ "is-unc-path": "^1.0.0" } }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true, - "optional": true - }, "is-unc-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", @@ -3337,6 +2652,12 @@ "unc-path-regex": "^0.1.2" } }, + "is-what": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz", + "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==", + "dev": true + }, "is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", @@ -3355,13 +2676,6 @@ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", "dev": true }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "dev": true, - "optional": true - }, "jquery": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.5.0.tgz", @@ -3377,47 +2691,6 @@ "esprima": "^4.0.0" } }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true, - "optional": true - }, - "json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", - "dev": true, - "optional": true - }, - "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, - "optional": true - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true, - "optional": true - }, - "jsprim": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", - "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", - "dev": true, - "optional": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" - } - }, "kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -3425,20 +2698,20 @@ "dev": true }, "less": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/less/-/less-3.9.0.tgz", - "integrity": "sha512-31CmtPEZraNUtuUREYjSqRkeETFdyEHSEPAGq4erDlUXtda7pzNmctdljdIagSb589d/qXGWiiP31R5JVf+v0w==", + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/less/-/less-3.13.1.tgz", + "integrity": "sha512-SwA1aQXGUvp+P5XdZslUOhhLnClSLIjWvJhmd+Vgib5BFIr9lMNlQwmwUNOjXThF/A0x+MCYYPeWEfeWiLRnTw==", "dev": true, "requires": { - "clone": "^2.1.2", + "copy-anything": "^2.0.1", "errno": "^0.1.1", "graceful-fs": "^4.1.2", "image-size": "~0.5.0", + "make-dir": "^2.1.0", "mime": "^1.4.1", - "mkdirp": "^0.5.0", - "promise": "^7.1.1", - "request": "^2.83.0", - "source-map": "~0.6.0" + "native-request": "^1.0.5", + "source-map": "~0.6.0", + "tslib": "^1.10.0" }, "dependencies": { "source-map": { @@ -3516,6 +2789,17 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "optional": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, "make-iterator": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", @@ -3548,23 +2832,6 @@ "dev": true, "optional": true }, - "mime-db": { - "version": "1.38.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.38.0.tgz", - "integrity": "sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg==", - "dev": true, - "optional": true - }, - "mime-types": { - "version": "2.1.22", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.22.tgz", - "integrity": "sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog==", - "dev": true, - "optional": true, - "requires": { - "mime-db": "~1.38.0" - } - }, "minimatch": { "version": "3.0.8", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz", @@ -3574,23 +2841,6 @@ "brace-expansion": "^1.1.7" } }, - "minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", - "dev": true, - "optional": true - }, - "mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dev": true, - "optional": true, - "requires": { - "minimist": "^1.2.6" - } - }, "multimatch": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-2.1.0.tgz", @@ -3603,6 +2853,13 @@ "minimatch": "^3.0.0" } }, + "native-request": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/native-request/-/native-request-1.1.0.tgz", + "integrity": "sha512-uZ5rQaeRn15XmpgE0xoPL8YWqcX90VtCFglYwAgkvKM5e8fog+vePLAhHxuuv/gRkrQxIeh5U3q9sMNUrENqWw==", + "dev": true, + "optional": true + }, "nopt": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", @@ -3612,13 +2869,6 @@ "abbrev": "1" } }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "dev": true, - "optional": true - }, "object.defaults": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", @@ -3755,19 +3005,19 @@ "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=", "dev": true }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true, - "optional": true - }, "picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "optional": true + }, "pkg-up": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz", @@ -3788,16 +3038,6 @@ } } }, - "promise": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", - "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", - "dev": true, - "optional": true, - "requires": { - "asap": "~2.0.3" - } - }, "prr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", @@ -3805,27 +3045,6 @@ "dev": true, "optional": true }, - "psl": { - "version": "1.1.31", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz", - "integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==", - "dev": true, - "optional": true - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true, - "optional": true - }, - "qs": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", - "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", - "dev": true, - "optional": true - }, "rechoir": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", @@ -3835,35 +3054,6 @@ "resolve": "^1.9.0" } }, - "request": { - "version": "2.88.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", - "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", - "dev": true, - "optional": true, - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.0", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.4.3", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - } - }, "resolve": { "version": "1.22.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", @@ -3909,19 +3099,19 @@ "glob": "^7.1.3" } }, - "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==", - "dev": true, - "optional": true - }, "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "optional": true + }, "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -3934,24 +3124,6 @@ "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", "dev": true }, - "sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "dev": true, - "optional": true, - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - } - }, "strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", @@ -3994,42 +3166,11 @@ "is-number": "^7.0.0" } }, - "tough-cookie": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", - "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", - "dev": true, - "optional": true, - "requires": { - "psl": "^1.1.24", - "punycode": "^1.4.1" - }, - "dependencies": { - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true, - "optional": true - } - } - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "dev": true, - "optional": true + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true }, "typeahead": { "version": "0.2.2", @@ -4064,29 +3205,12 @@ "util-deprecate": "^1.0.2" } }, - "uri-js": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", - "dev": true, - "optional": true, - "requires": { - "punycode": "^2.1.0" - } - }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true }, - "uuid": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", - "dev": true, - "optional": true - }, "v8flags": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.2.0.tgz", @@ -4096,18 +3220,6 @@ "homedir-polyfill": "^1.0.1" } }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "dev": true, - "optional": true, - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, "which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", diff --git a/default-assets-package/package.json b/default-assets-package/package.json index 3ec400fe0..7990f1aff 100644 --- a/default-assets-package/package.json +++ b/default-assets-package/package.json @@ -26,7 +26,7 @@ "grunt-contrib-copy": "^1.0.0", "grunt-contrib-less": "^2.0.0", "grunt-exec": "3.0.0", - "jquery": "^3.4.0", + "jquery": "^3.5.1", "load-grunt-tasks": "^4.0.0", "octicons": "4.4.0", "timeago": "1.6.4" diff --git a/docker-compose.yml b/docker-compose.yml index 52c7929cd..a4d057139 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -29,7 +29,7 @@ services: - portal volumes: - portal_postgres_data:/var/lib/postgresql/data - - ./pg.sql:/docker-entrypoint-initdb.d/pg.sql + - ./data/pg.sql:/docker-entrypoint-initdb.d/pg.sql environment: POSTGRES_DB: portal-repos POSTGRES_USER: portal diff --git a/docs/api.md b/docs/api.md index 261e6b6aa..b912d44a9 100644 --- a/docs/api.md +++ b/docs/api.md @@ -147,7 +147,7 @@ This API will retrieve information about a specific user. The first API version Where `login` is a GitHub username, case insensitive. -##### Response +##### Response: by GitHub username If a link is not found for the GitHub user @@ -188,7 +188,7 @@ This API returns an array if there is at least one matching account or accounts. Where `id` is an AAD ID. -##### Response +##### Response: by AAD ID If a link is not registered for this user @@ -233,7 +233,7 @@ Required API scope: `link` > POST /api/people/links -#### Request +#### Request: create a link ```text BODY @@ -249,7 +249,7 @@ BODY > If the account is a Service Account, the `corporate` object should also include a field called `serviceAccountMail` that points to a contact for the service account. -#### Response +#### Response: create a link ```http Status: 201 OK @@ -261,7 +261,7 @@ Status: 201 OK ### Create a repo -> This API requires that your API key be authorized for the `createRepo` scope +> This API requires that your API key be authorized for the `repo/create` scope This example uses a pure POST request plus headers for authorization: diff --git a/docs/configuration.md b/docs/configuration.md index 53cd38b68..a2ce8965a 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -85,7 +85,7 @@ Here is a short overview about the meanings of the different parameters: - **setupbycorporateusername** (string) - Username (from the corporate identity system) of the user who set up the organization - **setupbycorporateid** (string) - Unique identifier (from the corporate identity system) for the user who set up the organization - **setupbycorporatedisplayname** (string) - Display name (from the corporate identity system) for the user who set up the organization -- **specialteams** (object{specialTeam: string, teamId: integer}) - Special team configuration for the organization supported values for `specialTeam` types are: `everyone, sudo, globalSudo, systemWrite, systemRead, systemAdmin`. The `teamId` is the GitHub team ID for the special team. +- **specialteams** (object{specialTeam: string, teamId: integer}) - Special team configuration for the organization supported values for `specialTeam` types are: `everyone, sudo, globalSudo, systemWrite, systemRead, systemAdmin, openAccess`. The `teamId` is the GitHub team ID for the special team. ### Static settings @@ -125,13 +125,13 @@ The opensource-portal can also be configured to read organization configurations ## PostgreSQL Configuration -To run the opensource-portal with a postgres database, you need to [setup postgres](https://www.postgresql.org/docs/11/runtime.html) and initialize the database by running the `pg.sql`-file in the psql-terminal. +To run the opensource-portal with a postgres database, you need to [setup postgres](https://www.postgresql.org/docs/11/runtime.html) and initialize the database by running the `data/pg.sql`-file in the psql-terminal. It's recommended to [run postgres in a docker container](https://hub.docker.com/_/postgres), there is also an official docker image called `postgres` for building. Once the setup is done, set the `host`, `database`, `user`, `password`, `ssl` (as boolean) and `port` of the postgres in the `config/data.postgres.json`-file. -Additionally set the name of the linking-table (`tableName` parameter), if the tables were created with the `pg.sql`-file, the name for this table is `links`. +Additionally set the name of the linking-table (`tableName` parameter), if the tables were created with the `data/pg.sql`-file, the name for this table is `links`. -There is also a script in the `scripts` folder that can blast the `pg.sql` insertions into a new database. Be +There is also a script in the `scripts` folder that can blast the `data/pg.sql` insertions into a new database. Be sure to configure grants and your user accounts with the concept of least privilege required. ## Cache Configuration diff --git a/docs/jobs.md b/docs/jobs.md deleted file mode 100644 index 56173c8fc..000000000 --- a/docs/jobs.md +++ /dev/null @@ -1,20 +0,0 @@ -# Jobs - -There are automated cronjobs available to help keep things running smoothly, -if you choose to use them. - -Jobs are an alternate entrypoint into the application, and have full use of -the same set of [providers](./providers.md). - -## list of cronjobs - -Several jobs are available in the container or the `jobs/` folder. These can -optionally provide useful operational and services support. Often a Kubernetes -CronJob can help. - -- `cleanupInvites`: if configured for an org, cleanup old unaccepted invites -- `firehose`: ongoing processing of GitHub events for keeping cache up-to-date -- `managers`: cache the last-known manager for links, to use in notifications after a departure may remove someone from the graph -- `permissions`: updating permissions for all-write/all-read/all-admin teams when configured -- `refreshUsernames`: keeping link data fresh with GitHub username renames, corporate username and display name updates, and removing links for deleted GitHub users who remove their accounts permanently from GitHub.com -- `reports`: processing the building of report data about use, abandoned repos, etc. **this job is broken** diff --git a/docs/scripts.md b/docs/scripts.md deleted file mode 100644 index 25064f56b..000000000 --- a/docs/scripts.md +++ /dev/null @@ -1,5 +0,0 @@ -# Scripts - -> this file is incomplete - -- `migrateLinks`: a one-time migration script to help when moving link source of truth diff --git a/docs/service-dependencies.md b/docs/service-dependencies.md new file mode 100644 index 000000000..69269e5ce --- /dev/null +++ b/docs/service-dependencies.md @@ -0,0 +1,26 @@ +# Service dependencies + +_This content was moved from the `README.md` to reduce clutter. More content would be helpful._ + +- GitHub organization(s) +- Hosting environment +- Background job environment for eventual consistency work and maintenance cronjobs +- Daemon hosting for near-real-time process +- Queue system +- A cache system or multi-tiered cache implementation +- Azure Active Directory and the Microsoft Graph +- An email service to send mail +- Optional insights or telemetry system + +## Source of truth store + +The backend maintains in a data store of your choice key metadata for +repositories, links, and general compliance info. The backend supports +natively Azure Storage, Azure Table, Azure CosmosDB, and Postgres. + +We use **Postgres** for source of truth including: + +- GitHub organization configuration +- corporate GitHub repository metadata +- corporate identity-to-GitHub login links +- compliance metadata (enable/disabled repos) diff --git a/features/graphTeamSync.ts b/features/graphTeamSync.ts deleted file mode 100644 index e1c8c4777..000000000 --- a/features/graphTeamSync.ts +++ /dev/null @@ -1,31 +0,0 @@ -// -// Copyright (c) Microsoft. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. -// - -import { Team } from '../business'; -import { IProviders } from '../interfaces'; - -export class GraphTeamSync { - #team: Team; - #providers: IProviders; - - constructor(providers: IProviders, team: Team) { - this.#providers = providers; - this.#team = team; - } - - // On the GitHub side, they define: - // - The team must not have any sub-teams (must be a leaf node) - // - Can have up to 5 directory groups assigned to the team - // - Team Maintainers are not touched (TBD need to confirm) - // - If you suspend sync, it removes all the members - - // Unknowns: - // - What happens if the sole maint leaves... guess it just becomes an orphan team - // - How to handle removed directory groups - // - How much to store, log, show in the UI, or notify people about - - // Value-adds: - // - When a user links for the first time, kick off a job to evaluate the directory groups they're in -} diff --git a/index.ts b/index.ts new file mode 100644 index 000000000..b90109b36 --- /dev/null +++ b/index.ts @@ -0,0 +1,97 @@ +// +// Copyright (c) Microsoft. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +import express from 'express'; +import Debug from 'debug'; + +import type { ExecutionEnvironment, IReposApplication, SiteConfiguration } from './interfaces'; +import configResolver from './lib/config'; +import initialize from './middleware/initialize'; + +export * from './interfaces'; + +type InitializeCall = ( + executionEnvironment: ExecutionEnvironment, + config: SiteConfiguration, + configurationError: Error +) => Promise; + +export function createExpressApplication(): IReposApplication { + Debug.debug('startup')('starting web framework...'); + const app = express() as any as IReposApplication; + + app.initializeApplication = initializeApp.bind(undefined, app, express, __dirname); + app.startupApplication = commonStartup.bind( + undefined, + app.initializeApplication, + false /* not a job */, + true /* enable all apps */, + app + ); + + return app; +} + +function initializeApp( + app: IReposApplication, + express: any, + dirname: string, + executionEnvironment: ExecutionEnvironment, + config: SiteConfiguration, + configurationError: Error +) { + return initialize(executionEnvironment, app, express, dirname, config, configurationError); +} + +export async function commonStartup( + call: InitializeCall, + isJob: boolean, + enableAllGitHubApps: boolean, + app?: IReposApplication, + entrypointName?: string +) { + const executionEnvironment: ExecutionEnvironment = { + isJob, + enableAllGitHubApps, + entrypointName, + // + expressApplication: app, + // + providers: undefined, + skipModules: new Set(), + // + started: new Date(), + }; + + let painlessConfigResolver = null; + try { + painlessConfigResolver = configResolver(); + } catch (error) { + console.warn('Painless config resolver initialization error:'); + console.error(error); + throw error; + } + let config: any = null; + let configurationError: Error = null; + try { + config = await painlessConfigResolver.resolve(); + } catch (error) { + configurationError = error; + } + if (isJob) { + executionEnvironment.skipModules.add('web'); + } + try { + await call(executionEnvironment, config, configurationError); + } catch (startupError) { + console.error(`Startup error: ${startupError}`); + if (startupError.stack) { + console.error(startupError.stack); + } + process.exit(1); + } + + return executionEnvironment; +} diff --git a/interfaces/app.ts b/interfaces/app.ts index 7520ed409..ba5d2f2d0 100644 --- a/interfaces/app.ts +++ b/interfaces/app.ts @@ -3,14 +3,21 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Application } from 'express'; -import { IProviders } from './providers'; +import { Application, Response, NextFunction } from 'express'; -import type { RuntimeConfiguration } from './config'; +import type { IProviders } from './providers'; +import type { RuntimeConfiguration, SiteConfiguration } from './config'; +import type { ReposAppRequest } from './web'; -export interface IApplicationProfile { +export type ApplicationProfile = { applicationName: string; - customErrorHandlerRender?: (errorView: any, err: Error, req: any, res: any, next: any) => Promise; + customErrorHandlerRender?: ( + errorView: unknown, + err: Error, + req: ReposAppRequest, + res: Response, + next: NextFunction + ) => Promise; customRoutes?: () => Promise; logDependencies: boolean; serveClientAssets: boolean; @@ -19,7 +26,7 @@ export interface IApplicationProfile { startup?: (providers: IProviders) => Promise; sessions: boolean; webServer: boolean; -} +}; export interface IReposApplication extends Application { // Standard Express @@ -27,29 +34,50 @@ export interface IReposApplication extends Application { // Local things providers: IProviders; - config: any; + config: SiteConfiguration; isBackgroundJob: boolean; enableAllGitHubApps: boolean; runtimeConfiguration: RuntimeConfiguration; + executionEnvironment: ExecutionEnvironment; + startServer: () => Promise; - initializeApplication: (config: any, configurationError: Error) => Promise; - initializeJob: (config: any, configurationError: Error) => Promise; + initializeApplication: ( + executionEnvironment: ExecutionEnvironment, + config: SiteConfiguration, + configurationError: Error + ) => Promise; + startupApplication: () => Promise; - startupJob: () => Promise; runJob: ( job: (job: IReposJob) => Promise, options?: IReposJobOptions - ) => Promise; + ) => Promise; } +export type ExecutionEnvironment = { + isJob: boolean; + enableAllGitHubApps: boolean; + + expressApplication: IReposApplication | null; + + providers: IProviders; + skipModules: Set; + + entrypointName: string; + + started: Date; +}; + export interface IReposJob { app: IReposApplication; started: Date; providers: IProviders; parameters: any; args: string[]; + + executionEnvironment: ExecutionEnvironment; } export interface IReposJobResult { @@ -62,4 +90,5 @@ export interface IReposJobOptions { insightsPrefix?: string; parameters?: any; enableAllGitHubApps?: boolean; + name?: string; } diff --git a/interfaces/companySpecific/administration.ts b/interfaces/companySpecific/administration.ts index 9d138e664..d404b5275 100644 --- a/interfaces/companySpecific/administration.ts +++ b/interfaces/companySpecific/administration.ts @@ -3,7 +3,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import { IDictionary } from '../../interfaces'; diff --git a/interfaces/companySpecific/events/index.ts b/interfaces/companySpecific/events/index.ts new file mode 100644 index 000000000..016caa017 --- /dev/null +++ b/interfaces/companySpecific/events/index.ts @@ -0,0 +1,12 @@ +// +// Copyright (c) Microsoft. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +import type { ICompanySpecificEventsLinking } from './linking'; + +export * from './linking'; + +export interface ICompanySpecificEvents { + linking?: ICompanySpecificEventsLinking; +} diff --git a/interfaces/companySpecific/events/linking.ts b/interfaces/companySpecific/events/linking.ts new file mode 100644 index 000000000..6f6a55f99 --- /dev/null +++ b/interfaces/companySpecific/events/linking.ts @@ -0,0 +1,11 @@ +// +// Copyright (c) Microsoft. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +import type { ICorporateLink, IProviders } from '../..'; + +export interface ICompanySpecificEventsLinking { + onLink?: (providers: IProviders, link: ICorporateLink) => Promise; + onUnlink?: (providers: IProviders, corporateId: string) => Promise; +} diff --git a/interfaces/companySpecific/features/augmentApiMetadata.ts b/interfaces/companySpecific/features/augmentApiMetadata.ts index 9dab4d716..9dc19e10c 100644 --- a/interfaces/companySpecific/features/augmentApiMetadata.ts +++ b/interfaces/companySpecific/features/augmentApiMetadata.ts @@ -3,13 +3,20 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Organization } from '../../../business'; -import { IProviders } from '../../../interfaces'; +import { Organization, Team } from '../../../business'; +import type { IProviders, TeamJsonFormat } from '../../../interfaces'; export interface ICompanySpecificAugmentApiMetadata { - augmentOrganizationClientJson: ( + augmentOrganizationClientJson?: ( providers: IProviders, organization: Organization, - standardJsonMetadata: any - ) => any; + standardJsonMetadata: object + ) => object; + + augmentTeamClientJson?: ( + providers: IProviders, + team: Team, + standardJsonMetadata: object, + jsonFormat: TeamJsonFormat + ) => Promise; } diff --git a/interfaces/companySpecific/features/firehose.ts b/interfaces/companySpecific/features/firehose.ts index 90c3213f0..63656249c 100644 --- a/interfaces/companySpecific/features/firehose.ts +++ b/interfaces/companySpecific/features/firehose.ts @@ -4,9 +4,10 @@ // import { IGitHubAppInstallation, IGitHubWebhookEnterprise, IProviders } from '../../../interfaces'; +import type { WebhookProcessor } from '../../../business/webhooks/organizationProcessor'; export interface ICompanySpecificFeatureFirehose { - processWebhook: ( + processWebhook?: ( providers: IProviders, body: any, eventType: string, @@ -14,4 +15,6 @@ export interface ICompanySpecificFeatureFirehose { installation: IGitHubAppInstallation, acknowledgeEvent: () => void ) => Promise; + + getAdditionalWebhookTasks?: (providers: IProviders) => Promise; } diff --git a/interfaces/companySpecific/features/index.ts b/interfaces/companySpecific/features/index.ts index 2c821d92b..1a3ca4f66 100644 --- a/interfaces/companySpecific/features/index.ts +++ b/interfaces/companySpecific/features/index.ts @@ -3,14 +3,14 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { ICompanySpecificAugmentApiMetadata } from './augmentApiMetadata'; -import { ICompanySpecificFeatureDemo } from './demo'; -import { ICompanySpecificFeatureFirehose } from './firehose'; -import { ICompanySpecificFeatureMailProvider } from './mailProvider'; -import { ICompanySpecificFeatureOrganizationJoinAcl } from './organizationJoinAcl'; -import { ICompanySpecificFeatureOrganizationSudo } from './organizationSudo'; -import { ICompanySpecificFeaturePortalSudo } from './portalSudo'; -import { ICompanySpecificFeatureRepositoryState } from './repositoryActions'; +import type { ICompanySpecificAugmentApiMetadata } from './augmentApiMetadata'; +import type { ICompanySpecificFeatureDemo } from './demo'; +import type { ICompanySpecificFeatureFirehose } from './firehose'; +import type { ICompanySpecificFeatureMailProvider } from './mailProvider'; +import type { ICompanySpecificFeatureOrganizationJoinAcl } from './organizationJoinAcl'; +import type { ICompanySpecificFeatureOrganizationSudo } from './organizationSudo'; +import type { ICompanySpecificFeaturePortalSudo } from './portalSudo'; +import type { ICompanySpecificFeatureRepositoryState } from './repositoryActions'; export * from './organizationSudo'; export * from './portalSudo'; @@ -22,7 +22,7 @@ export * from './organizationJoinAcl'; export * from './augmentApiMetadata'; export interface ICompanySpecificFeatures { - augmentApiMetadata: ICompanySpecificAugmentApiMetadata; + augmentApiMetadata?: ICompanySpecificAugmentApiMetadata; organizationSudo?: ICompanySpecificFeatureOrganizationSudo; organizationJoinAcl?: ICompanySpecificFeatureOrganizationJoinAcl; portalSudo?: ICompanySpecificFeaturePortalSudo; diff --git a/interfaces/companySpecific/features/organizationSudo.ts b/interfaces/companySpecific/features/organizationSudo.ts index c7c25bc29..292df91e6 100644 --- a/interfaces/companySpecific/features/organizationSudo.ts +++ b/interfaces/companySpecific/features/organizationSudo.ts @@ -4,7 +4,7 @@ // import { Organization } from '../../../business'; -import { IOrganizationSudo } from '../../../features'; +import { IOrganizationSudo } from '../../../business/features'; import { IProviders } from '../../../interfaces'; export interface ICompanySpecificFeatureOrganizationSudo { diff --git a/interfaces/companySpecific/features/portalSudo.ts b/interfaces/companySpecific/features/portalSudo.ts index 71f6c25ad..07a704eb7 100644 --- a/interfaces/companySpecific/features/portalSudo.ts +++ b/interfaces/companySpecific/features/portalSudo.ts @@ -3,7 +3,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { IPortalSudo } from '../../../features'; +import { IPortalSudo } from '../../../business/features'; import { IProviders } from '../../../interfaces'; export interface ICompanySpecificFeaturePortalSudo { diff --git a/interfaces/companySpecific/index.ts b/interfaces/companySpecific/index.ts index 3bf21513e..fb311f15b 100644 --- a/interfaces/companySpecific/index.ts +++ b/interfaces/companySpecific/index.ts @@ -11,3 +11,4 @@ export * from './administration'; export * from './passport'; export * from './views'; export * from './urls'; +export * from './events'; diff --git a/interfaces/companySpecific/middleware.ts b/interfaces/companySpecific/middleware.ts index ceb754970..f7c9e5e50 100644 --- a/interfaces/companySpecific/middleware.ts +++ b/interfaces/companySpecific/middleware.ts @@ -9,6 +9,7 @@ import { IProviders, ReposAppRequest } from '../../interfaces'; import { IndividualContext } from '../../business/user'; import { IRequestTeamPermissions } from '../../middleware/github/teamPermissions'; import type { ApiClientGroupDisplay } from '../api'; +import { ITeamJoinRequestSubmitOutcome } from '../../routes/org/team'; export interface ICompanySpecificRepoPermissionsMiddlewareCalls { afterPermissionsInitialized?: ( @@ -36,6 +37,11 @@ export interface ICompanySpecificTeamPermissionsMiddlewareCalls { activeContext: IndividualContext, team: Team ) => Promise; + beforeJoinRequest?: ( + providers: IProviders, + activeContext: IndividualContext, + team: Team + ) => Promise; } export interface ICompanySpecificAuthenticationCalls { @@ -46,8 +52,8 @@ export interface ICompanySpecificAuthenticationCalls { export interface IAadAuthenticationValidator { isAuthorizedTenant(tenantId: string): Promise; getAudienceIdentities(): Promise; - getAuthorizedClientIdToken(clientId: string): Promise; - getAuthorizedObjectIdToken(objectId: string): Promise; + getAuthorizedClientIdToken(clientId: string): Promise; + getAuthorizedObjectIdToken(objectId: string): Promise; getScopes(tokenRepresentation: any): Promise; getDisplayValues(tokenRepresentation: any): Promise; } diff --git a/interfaces/companySpecific/passport.ts b/interfaces/companySpecific/passport.ts index 389e08065..478aa6c60 100644 --- a/interfaces/companySpecific/passport.ts +++ b/interfaces/companySpecific/passport.ts @@ -4,7 +4,7 @@ // import { PassportStatic } from 'passport'; -import { IAuthenticationHelperMethods } from '../../middleware/passport-routes'; +import type { IAuthenticationHelperMethods } from '../../middleware/passport-routes'; export interface ICompanySpecificPassportMiddleware { configure?: (app: any, config: any, passport: PassportStatic) => void; diff --git a/interfaces/companySpecific/routes/api/index.ts b/interfaces/companySpecific/routes/api/index.ts index a8c324252..0f5a5fae1 100644 --- a/interfaces/companySpecific/routes/api/index.ts +++ b/interfaces/companySpecific/routes/api/index.ts @@ -22,6 +22,7 @@ export interface IAttachCompanySpecificRoutesApiContextual { export interface IAttachCompanySpecificRoutesApiContextualOrganization { index?: ConnectRouter; repo?: ConnectRouter; + team?: ConnectRouter; } export interface IAttachCompanySpecificRoutesApiContextualAdministration { diff --git a/interfaces/companySpecific/routes/index.ts b/interfaces/companySpecific/routes/index.ts index 9e4560744..ddb835ff4 100644 --- a/interfaces/companySpecific/routes/index.ts +++ b/interfaces/companySpecific/routes/index.ts @@ -3,7 +3,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import { IAttachCompanySpecificRoutesApi } from './api'; export * from './api'; diff --git a/interfaces/github/account.ts b/interfaces/github/account.ts index c57efafa5..f4bf943e3 100644 --- a/interfaces/github/account.ts +++ b/interfaces/github/account.ts @@ -5,6 +5,7 @@ export enum AccountJsonFormat { GitHub = 'github', + GitHubExtended = 'github+extended', UplevelWithLink = 'github+link', GitHubDetailedWithLink = 'detailed+link', } diff --git a/interfaces/github/collaborators.ts b/interfaces/github/collaborators.ts index 7e9fcf2c7..5ab70be9c 100644 --- a/interfaces/github/collaborators.ts +++ b/interfaces/github/collaborators.ts @@ -5,7 +5,8 @@ export interface IGitHubCollaboratorPermissions { admin: boolean; - pull: boolean; + maintain: boolean; push: boolean; - // triage and maintain do not appear today by the GitHub API (sigh), it's in V4 GraphQL but not in V3 REST + triage: boolean; + pull: boolean; } diff --git a/interfaces/github/operations.ts b/interfaces/github/operations.ts index e2998f3c6..4ac9c3ee8 100644 --- a/interfaces/github/operations.ts +++ b/interfaces/github/operations.ts @@ -3,9 +3,15 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { CoreCapability, ICacheDefaultTimes, IPurposefulGetAuthorizationHeader, ICacheOptions } from '.'; +import { + CoreCapability, + ICacheDefaultTimes, + PurposefulGetAuthorizationHeader, + ICacheOptions, + GetAuthorizationHeader, +} from '.'; import { IProviders, ICorporateLink, ICachedEmployeeInformation } from '..'; -import { IRepositoryMetadataProvider } from '../../entities/repositoryMetadata/repositoryMetadataProvider'; +import { IRepositoryMetadataProvider } from '../../business/entities/repositoryMetadata/repositoryMetadataProvider'; import { RestLibrary } from '../../lib/github'; import { Account } from '../../business'; @@ -26,18 +32,34 @@ export interface IOperationsProviders { providers: IProviders; } +export type LinkEvent = ICorporateLink & { + linkId: string; + correlationId: string; +}; + +export type UnlinkEvent = { + github: { + id: number; + login: string; + }; + aad: { + preferredName: string; + userPrincipalName: string; + id: string; + }; +}; + export interface IOperationsLinks { getLinks(options?: any): Promise; getLinkByThirdPartyId(thirdPartyId: string): Promise; getLinkByThirdPartyUsername(username: string): Promise; tryGetLink(login: string): Promise; - fireLinkEvent(value): Promise; - fireUnlinkEvent(value): Promise; + fireLinkEvent(value: LinkEvent): Promise; + fireUnlinkEvent(value: UnlinkEvent): Promise; } export interface IOperationsNotifications { getOperationsMailAddress(): string; - getInfrastructureNotificationsMail(): string; getLinksNotificationMailAddress(): string; getRepositoriesNotificationMailAddress(): string; } @@ -79,8 +101,9 @@ export interface IOperationsLockdownFeatureFlags { } export interface IOperationsCentralOperationsToken { - getCentralOperationsToken(): IPurposefulGetAuthorizationHeader; // IGetAuthorizationHeader ?; getAccountByUsername(username: string, options?: ICacheOptions): Promise; + getPublicReadOnlyStaticToken(): GetAuthorizationHeader; + getPublicAuthorizationToken(): GetAuthorizationHeader; } export function operationsIsCapable( diff --git a/interfaces/github/orgs.ts b/interfaces/github/orgs.ts index 52092fda5..b815b973e 100644 --- a/interfaces/github/orgs.ts +++ b/interfaces/github/orgs.ts @@ -3,8 +3,8 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { ICacheOptions, IPagedCacheOptions } from '.'; -import { ICorporateLink } from '..'; +import type { ICacheOptions, IPagedCacheOptions } from '.'; +import type { ICorporateLink } from '..'; import { OrganizationMember } from '../../business'; import { Repository } from '../../business/repository'; @@ -97,3 +97,24 @@ export interface IOrganizationMembership { organization: any; user: any; } + +export type GitHubSimpleAccount = { + login: string; + avatar_url: string; + id: number; +}; + +export type GitHubOrganizationInvite = { + created_at: string; + email: string; + failed_at: string; + failed_reason: string; + id: number; + invitation_source: string; // 'member' + invitation_teams_url: string; + inviter: GitHubSimpleAccount; + login: string; + node_id: string; + role: string; // 'direct_member' + team_count: number; +}; diff --git a/interfaces/github/repos.ts b/interfaces/github/repos.ts index 976f42b00..828af3ba5 100644 --- a/interfaces/github/repos.ts +++ b/interfaces/github/repos.ts @@ -4,18 +4,20 @@ // import type { ICacheOptions, IPagedCacheOptions, IAccountBasics, IGitHubTeamBasics } from '.'; +import type { IGitHubWebhookEnterprise } from './apps'; import { IPersonalizedUserAggregateRepositoryPermission, TeamRepositoryPermission, GraphManager, } from '../../business'; -import type { IRepositoryMetadataProvider } from '../../entities/repositoryMetadata/repositoryMetadataProvider'; +import type { IRepositoryMetadataProvider } from '../../business/entities/repositoryMetadata/repositoryMetadataProvider'; import { GitHubPullRequestState, GitHubPullRequestSort, GitHubSortDirection, } from '../../lib/github/collections'; import type { IRequestTeamPermissions } from '../../middleware/github/teamPermissions'; +import { CreateError } from '../../lib/transitional'; export enum GitHubRepositoryPermission { Pull = 'pull', @@ -174,7 +176,49 @@ export interface IGitHubBranchDetailed { }; } -export interface IRepositoryBranchAccessProtections { +export type RepositoryBranchAccessProtections = { + required_signatures: { + enabled: boolean; + }; + allow_fork_syncing: { + enabled: boolean; + }; + lock_branch: { + enabled: boolean; + }; + required_conversation_resolution: { + enabled: boolean; + }; + block_creations: { + enabled: boolean; + }; + required_pull_request_reviews: { + dismissal_restrictions: + | { + users: IAccountBasics[]; + teams: IGitHubTeamBasics[]; + apps: IGitHubWebhookEnterprise[]; + } + | Record; + dismiss_stale_reviews: boolean; + require_code_owner_reviews: boolean; + required_approving_review_count: number; + require_last_push_approval: boolean; + bypass_pull_request_allowances: { + users: IAccountBasics[]; + teams: IGitHubTeamBasics[]; + apps: IGitHubWebhookEnterprise[]; + }; + } | null; + required_status_checks: { + strict: boolean; + contexts: string[]; + checks: { + context: string; + app_id: number; + }[]; + } | null; + branch?: string; allow_deletions: { enabled: boolean; }; @@ -194,7 +238,7 @@ export interface IRepositoryBranchAccessProtections { apps: unknown[]; }; url: string; -} +}; export interface ITemporaryCommandOutput { error?: Error; @@ -219,7 +263,9 @@ export interface IRepositorySearchOptions { export enum GitHubCollaboratorPermissionLevel { Admin = 'admin', + Maintain = 'maintain', Write = 'write', + Triage = 'triage', Read = 'read', None = 'none', } @@ -232,13 +278,119 @@ export function ConvertGitHubCollaboratorPermissionLevelToGitHubRepositoryPermis return null; case GitHubCollaboratorPermissionLevel.Admin: return GitHubRepositoryPermission.Admin; + case GitHubCollaboratorPermissionLevel.Maintain: + return GitHubRepositoryPermission.Maintain; case GitHubCollaboratorPermissionLevel.Write: return GitHubRepositoryPermission.Push; + case GitHubCollaboratorPermissionLevel.Triage: + return GitHubRepositoryPermission.Triage; case GitHubCollaboratorPermissionLevel.Read: return GitHubRepositoryPermission.Pull; default: - throw new Error( - `ConvertGitHubCollaboratorPermissionLevelToGitHubRepositoryPermission unrecognized value ${level} cannot be translated` + throw CreateError.InvalidParameters( + `Unrecognized GitHub permission value ${level}, current value cannot be translated (ConvertGitHubCollaboratorPermissionLevelToGitHubRepositoryPermission)` ); } } + +export enum GitHubAdvancedSecurityFeatureState { + Enabled = 'enabled', + Disabled = 'disabled', +} + +export type GitHubAdvancedSecurityFeatureStatusValue = { + status: GitHubAdvancedSecurityFeatureState; +}; + +export type GitHubSecurityAnalysisFeatures = { + advanced_security: GitHubAdvancedSecurityFeatureStatusValue; + secret_scanning: GitHubAdvancedSecurityFeatureStatusValue; + secret_scanning_push_protection: GitHubAdvancedSecurityFeatureStatusValue; + dependabot_security_updates: GitHubAdvancedSecurityFeatureStatusValue; + secret_scanning_validity_checks: GitHubAdvancedSecurityFeatureStatusValue; +}; + +export enum GitHubRepositoryOwnerType { + Organization = 'Organization', + User = 'User', +} + +export type GitHubRepositoryOwner = { + login: string; + id: number; + node_id: string; + avatar_url: string; + url: string; + type: GitHubRepositoryOwnerType; +}; + +export type GitHubRepositoryLicense = { + key: string; + name: string; + spdx_id: string; + url: string; + node_id: string; +}; + +export type GitHubRepositoryDetails = { + id: number; + node_id: string; + name: string; + full_name: string; + private: boolean; + owner: GitHubRepositoryOwner; + html_url: string; + description: string; + fork: boolean; + url: string; + created_at: string; + updated_at: string; + pushed_at: string; + git_url: string; + ssh_url: string; + clone_url: string; + homepage: string | null; + size: number; + stargazers_count: number; + watchers_count: number; + language: string; + has_issues: boolean; + has_projects: boolean; + has_downloads: boolean; + has_wiki: boolean; + has_pages: boolean; + has_discussions: boolean; + forks_count: number; + archived: boolean; + disabled: boolean; + open_issues_count: number; + license?: GitHubRepositoryLicense; + allow_forking: boolean; + is_template: boolean; + web_commit_signoff_required: boolean; + topics: string[]; + visibility?: GitHubRepositoryVisibility; + forks: number; + open_issues: number; + watchers: number; + default_branch: string; + // permissions: admin / maintain / push / triage / pull + // temp_clone_token: ... + allow_squash_merge: boolean; + allow_merge_commit: boolean; + allow_rebase_merge: boolean; + allow_auto_merge: boolean; + delete_branch_on_merge: boolean; + allow_update_branch: boolean; + use_squash_pr_title_as_default: boolean; + squash_merge_commit_message: string; + squash_merge_commit_title: string; + merge_commit_message: string; + merge_commit_title: string; + template_repository: GitHubRepositoryDetails; + organization?: GitHubRepositoryOwner; + security_and_analysis?: GitHubSecurityAnalysisFeatures; + network_count: number; + subscribers_count: number; + parent?: GitHubRepositoryDetails; +}; diff --git a/interfaces/github/rest.ts b/interfaces/github/rest.ts index c8ed62096..9d43fc236 100644 --- a/interfaces/github/rest.ts +++ b/interfaces/github/rest.ts @@ -3,7 +3,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { AppPurposeTypes } from '../../business/githubApps'; +import { AppPurposeTypes } from '../../lib/github/appPurposes'; export interface ICacheOptions { backgroundRefresh?: any | null | undefined; @@ -14,25 +14,29 @@ export interface ICacheOptionsWithPurpose extends ICacheOptions { purpose?: AppPurposeTypes; } +export type WithOptionalPurpose = { + purpose?: AppPurposeTypes; +}; + export interface IPagedCacheOptions extends ICacheOptions { pageRequestDelay?: number | null | undefined; // FUTURE: could be a function, too } -export interface IPurposefulGetAuthorizationHeader { - (purpose: AppPurposeTypes): Promise; -} +export type PagedCacheOptionsWithPurpose = IPagedCacheOptions & WithOptionalPurpose; -export interface IGetAuthorizationHeader { - (): Promise; -} +export type PurposefulGetAuthorizationHeader = ( + purpose: AppPurposeTypes +) => Promise; + +export type GetAuthorizationHeader = () => Promise; -export interface IAuthorizationHeaderValue { +export type AuthorizationHeaderValue = { value: string; purpose: AppPurposeTypes; source?: string; installationId?: number; organizationName?: string; -} +}; export interface ICacheDefaultTimes { orgReposStaleSeconds: number; @@ -58,6 +62,7 @@ export interface ICacheDefaultTimes { teamDetailStaleSeconds: number; orgRepoWebhooksStaleSeconds: number; teamRepositoryPermissionStaleSeconds: number; + defaultStaleSeconds: number; } // These "core capabilities" were created when the GitHub operations diff --git a/interfaces/github/teams.ts b/interfaces/github/teams.ts index 110ddb0f7..e6d8a417a 100644 --- a/interfaces/github/teams.ts +++ b/interfaces/github/teams.ts @@ -22,6 +22,29 @@ export interface IGitHubTeamBasics { slug: string; } +export enum GitHubTeamNotificationSetting { + Enabled = 'notification_enabled', + Disabled = 'notification_disabled', +} + +export type GitHubTeamDetails = IGitHubTeamBasics & { + node_id: string; + description: string; + privacy: GitHubTeamPrivacy; + notification_setting: GitHubTeamNotificationSetting; + url: string; + html_url: string; + members_url: string; + repositories_url: string; + // permission: ... + created_at: string; + updated_at: string; + members_count: number; + repos_count: number; + // organization: ... + parent: IGitHubTeamBasics; +}; + export enum GitHubRepositoryType { Sources = 'sources', } diff --git a/interfaces/index.ts b/interfaces/index.ts index 079d0b3d7..4d0713adc 100644 --- a/interfaces/index.ts +++ b/interfaces/index.ts @@ -16,22 +16,26 @@ export * from './providers'; export * from './web'; export * from './config'; -import { +import type { ExecutionEnvironment } from './app'; +import type { IAttachCompanySpecificRoutes, IAttachCompanySpecificMiddleware, ICorporationAdministrationSection, IAttachCompanySpecificStrings, ICompanySpecificFeatures, + ICompanySpecificEvents, IAttachCompanySpecificViews, IAttachCompanySpecificUrls, } from './companySpecific'; -import { ICompanySpecificPassportMiddleware } from './companySpecific/passport'; -import { IProviders } from './providers'; +import type { ICompanySpecificPassportMiddleware } from './companySpecific/passport'; +import type { SiteConfiguration } from './config'; +import type { IProviders } from './providers'; // We're great at long variable names! export interface ICompanySpecificStartupProperties { isCompanySpecific: true; + events?: ICompanySpecificEvents; routes?: IAttachCompanySpecificRoutes; middleware?: IAttachCompanySpecificMiddleware; administrationSection?: ICorporationAdministrationSection; @@ -42,6 +46,11 @@ export interface ICompanySpecificStartupProperties { urls?: IAttachCompanySpecificUrls; } -export type ICompanySpecificStartupFunction = (config: any, p: IProviders, rootdir: string) => Promise; +export type ICompanySpecificStartupFunction = ( + executionEnvironment: ExecutionEnvironment, + config: SiteConfiguration, + p: IProviders, + rootdir: string +) => Promise; export type ICompanySpecificStartup = ICompanySpecificStartupFunction & ICompanySpecificStartupProperties; diff --git a/interfaces/middleware.ts b/interfaces/middleware.ts new file mode 100644 index 000000000..8b8b44b2f --- /dev/null +++ b/interfaces/middleware.ts @@ -0,0 +1,11 @@ +// +// Copyright (c) Microsoft. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +import { UserSettings } from '../business/entities/userSettings'; +import type { ReposAppRequest } from './web'; + +export type ReposAppRequestWithUserSettings = ReposAppRequest & { + userSettings?: UserSettings; +}; diff --git a/interfaces/providers.ts b/interfaces/providers.ts index fbe0ae2e5..d438fbb1e 100644 --- a/interfaces/providers.ts +++ b/interfaces/providers.ts @@ -9,25 +9,25 @@ import redis, { RedisClientType } from 'redis'; import { Pool as PostgresPool } from 'pg'; import { - IApplicationProfile, + ApplicationProfile, ICorporationAdministrationSection, IReposApplication, SiteConfiguration, } from '.'; import { Operations } from '../business'; import QueryCache from '../business/queryCache'; -import { IAuditLogRecordProvider } from '../entities/auditLogRecord/auditLogRecordProvider'; -import { ILocalExtensionKeyProvider } from '../entities/localExtensionKey'; -import { IOrganizationMemberCacheProvider } from '../entities/organizationMemberCache/organizationMemberCacheProvider'; -import { IOrganizationSettingProvider } from '../entities/organizationSettings/organizationSettingProvider'; -import { IRepositoryCacheProvider } from '../entities/repositoryCache/repositoryCacheProvider'; -import { IRepositoryCollaboratorCacheProvider } from '../entities/repositoryCollaboratorCache/repositoryCollaboratorCacheProvider'; -import { IRepositoryTeamCacheProvider } from '../entities/repositoryTeamCache/repositoryTeamCacheProvider'; -import { ITeamCacheProvider } from '../entities/teamCache/teamCacheProvider'; -import { IApprovalProvider } from '../entities/teamJoinApproval/approvalProvider'; -import { ITeamMemberCacheProvider } from '../entities/teamMemberCache/teamMemberCacheProvider'; -import { ITokenProvider } from '../entities/token'; -import { IUserSettingsProvider } from '../entities/userSettings'; +import { IAuditLogRecordProvider } from '../business/entities/auditLogRecord/auditLogRecordProvider'; +import { ILocalExtensionKeyProvider } from '../business/entities/localExtensionKey'; +import { IOrganizationMemberCacheProvider } from '../business/entities/organizationMemberCache/organizationMemberCacheProvider'; +import { IOrganizationSettingProvider } from '../business/entities/organizationSettings/organizationSettingProvider'; +import { IRepositoryCacheProvider } from '../business/entities/repositoryCache/repositoryCacheProvider'; +import { IRepositoryCollaboratorCacheProvider } from '../business/entities/repositoryCollaboratorCache/repositoryCollaboratorCacheProvider'; +import { IRepositoryTeamCacheProvider } from '../business/entities/repositoryTeamCache/repositoryTeamCacheProvider'; +import { ITeamCacheProvider } from '../business/entities/teamCache/teamCacheProvider'; +import { IApprovalProvider } from '../business/entities/teamJoinApproval/approvalProvider'; +import { ITeamMemberCacheProvider } from '../business/entities/teamMemberCache/teamMemberCacheProvider'; +import { ITokenProvider } from '../business/entities/token'; +import { IUserSettingsProvider } from '../business/entities/userSettings'; import { ICacheHelper } from '../lib/caching'; import BlobCache from '../lib/caching/blob'; import { ICampaignHelper } from '../lib/campaigns'; @@ -37,18 +37,18 @@ import { ILinkProvider } from '../lib/linkProviders'; import { IMailAddressProvider } from '../lib/mailAddressProvider'; import { IMailProvider } from '../lib/mailProvider'; import { IQueueProcessor } from '../lib/queues'; -import { ICustomizedNewRepositoryLogic, ICustomizedTeamPermissionsWebhookLogic } from '../transitional'; +import { ICustomizedNewRepositoryLogic, ICustomizedTeamPermissionsWebhookLogic } from '../lib/transitional'; import { IEntityMetadataProvider } from '../lib/entityMetadataProvider'; -import { IRepositoryProvider } from '../entities/repository'; +import { IRepositoryProvider } from '../business/entities/repository'; import { IKeyVaultSecretResolver } from '../lib/keyVaultResolver'; -import { IOrganizationAnnotationMetadataProvider } from '../entities/organizationAnnotation'; +import { IOrganizationAnnotationMetadataProvider } from '../business/entities/organizationAnnotation'; import type { IImmutableStorageProvider } from '../lib/immutable'; type ProviderGenerator = (value: string) => IEntityMetadataProvider; export interface IProviders { app: IReposApplication; - applicationProfile: IApplicationProfile; + applicationProfile: ApplicationProfile; authorizationCodeClient?: AuthorizationCode; corporateAdministrationProfile?: ICorporationAdministrationSection; corporateViews?: any; diff --git a/interfaces/queryCache.ts b/interfaces/queryCache.ts index 658775053..9a8486eca 100644 --- a/interfaces/queryCache.ts +++ b/interfaces/queryCache.ts @@ -10,11 +10,11 @@ import { GitHubRepositoryPermission, } from '.'; import { Team, Repository, Organization } from '../business'; -import { RepositoryCacheEntity } from '../entities/repositoryCache/repositoryCache'; -import { RepositoryCollaboratorCacheEntity } from '../entities/repositoryCollaboratorCache/repositoryCollaboratorCache'; -import { RepositoryTeamCacheEntity } from '../entities/repositoryTeamCache/repositoryTeamCache'; -import { TeamCacheEntity } from '../entities/teamCache/teamCache'; -import { TeamMemberCacheEntity } from '../entities/teamMemberCache/teamMemberCache'; +import { RepositoryCacheEntity } from '../business/entities/repositoryCache/repositoryCache'; +import { RepositoryCollaboratorCacheEntity } from '../business/entities/repositoryCollaboratorCache/repositoryCollaboratorCache'; +import { RepositoryTeamCacheEntity } from '../business/entities/repositoryTeamCache/repositoryTeamCache'; +import { TeamCacheEntity } from '../business/entities/teamCache/teamCache'; +import { TeamMemberCacheEntity } from '../business/entities/teamMemberCache/teamMemberCache'; export enum QueryCacheOperation { New = 'new', diff --git a/interfaces/web.ts b/interfaces/web.ts index 18448f877..4657f0082 100644 --- a/interfaces/web.ts +++ b/interfaces/web.ts @@ -4,7 +4,7 @@ // import { Session } from 'express-session'; -import { Request, Response } from 'express'; +import { NextFunction, Request, Response } from 'express'; import { AccessToken } from 'simple-oauth2'; import type { TelemetryClient } from 'applicationinsights'; @@ -36,8 +36,12 @@ export enum LocalApiRepoAction { Delete = 'delete', Archive = 'archive', UnArchive = 'unarchive', + Privatize = 'privatize', } +export type VoidedExpressRoute = (req: ReposAppRequest, res: Response, next: NextFunction) => Promise; +// req: Request>, res: Response, number>, next: NextFunction) => void | Promise<...> + export interface ReposAppRequest extends Request { // passport isAuthenticated(): boolean; @@ -56,9 +60,9 @@ export interface ReposAppRequest extends Request { correlationId?: string; scrubbedUrl?: string; - // FUTURE: apiContext: IndividualContext; individualContext: IndividualContext; + watchdogContextOverride?: IndividualContext; oauthAccessToken: AccessToken; } diff --git a/job.ts b/job.ts new file mode 100644 index 000000000..24e77ef08 --- /dev/null +++ b/job.ts @@ -0,0 +1,182 @@ +// +// Copyright (c) Microsoft. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +import { hostname } from 'os'; +import Debug from 'debug'; + +import type { + ExecutionEnvironment, + IProviders, + IReposJob, + IReposJobOptions, + IReposJobResult, + SiteConfiguration, +} from './interfaces'; +import { commonStartup } from '.'; +import { quitInTenSeconds } from './lib/utils'; +import initialize from './middleware/initialize'; + +export async function runJob( + job: (job: IReposJob) => Promise, + options?: IReposJobOptions +): Promise { + Debug.debug('startup')('starting job...'); + + options = options || {}; + // TODO: automatically track elapsed job time + const started = new Date(); + if (options.timeoutMinutes) { + setTimeout( + () => { + // TODO: insights metric and event, if a prefix exists + console.log(`Kill bit at ${options.timeoutMinutes}m`); + process.exit(1); + }, + 1000 * 60 * options.timeoutMinutes + ); + } + if (options.defaultDebugOutput && !process.env.DEBUG) { + process.env.DEBUG = options.defaultDebugOutput; + } + + let executionEnvironment: ExecutionEnvironment = null; + try { + executionEnvironment = await commonStartup( + initializeJob, + true /* job */, + options.enableAllGitHubApps, + null /* app */, + options.name + ); + } catch (startupError) { + console.error(`Job startup error before runJob: ${startupError}`); + quitInTenSeconds(false); + return; + } + const providers = executionEnvironment?.providers; + if (options.insightsPrefix && providers?.insights) { + try { + providers?.insights?.trackEvent({ + name: `${options.insightsPrefix}Started`, + properties: { + hostname: hostname(), + }, + }); + } catch (ignoreInsightsError) { + console.error(`insights error: ${ignoreInsightsError}`); + } + } + const jobObject = { + app: providers?.app, + executionEnvironment, + providers, + started, + parameters: options && options.parameters ? options.parameters : {}, + args: process.argv.length > 2 ? process.argv.slice(2) : [], + }; + let result: IReposJobResult = null; + try { + result = (await job.call(null, jobObject)) as IReposJobResult; + if (result?.successProperties && providers?.insights && options.insightsPrefix) { + try { + providers?.insights?.trackEvent({ + name: `${options.insightsPrefix}Success`, + properties: Object.assign( + { + hostname: hostname(), + }, + result.successProperties + ), + }); + } catch (ignoreInsightsError) { + console.error(`insights error: ${ignoreInsightsError}`); + } + } + } catch (jobError) { + console.error(`The job failed: ${jobError}`); + if (jobError.stack) { + console.error(jobError.stack); + } + // by default, let's not show the whole inner error + const simpleError = { ...jobError }; + simpleError?.cause && delete simpleError.cause; + console.dir(simpleError); + const config = providers?.config; + quitInTenSeconds(false, config); + if (options.insightsPrefix) { + try { + providers?.insights?.trackException({ + exception: jobError, + properties: { + name: `${options.insightsPrefix}Failure`, + }, + }); + } catch (ignoreInsightsError) { + console.error(`insights error: ${ignoreInsightsError}`); + } + } + trySilentInsightsFlush(providers); + return result; + } + // CONSIDER: insights metric for job time + trySilentInsightsFlush(providers); + console.log(); + console.log('The job was successful.'); + quitInTenSeconds(true); + return result; +} + +function trySilentInsightsFlush(providers: IProviders) { + try { + providers?.insights?.flush(); + } catch (ignored) { + console.warn(ignored); + } +} + +function initializeJob( + executionEnvironment: ExecutionEnvironment, + config: SiteConfiguration, + configurationError: Error +) { + if (!config || configurationError) { + console.warn(`Configuration did not resolve successfully`, configurationError); + } + return initialize( + executionEnvironment, + null /* app */, + null /* express */, + __dirname, + config, + configurationError + ); +} + +export const job = { + runBackgroundJob: async ( + script: (providers: IProviders, jobParameters?: IReposJob) => Promise, + options?: IReposJobOptions + ) => { + return runJob( + async function (jobParameters: IReposJob) { + return (await script(jobParameters.providers, jobParameters)) || {}; + }, + Object.assign({ enableAllGitHubApps: false }, options || {}) + ); + }, + run: async ( + script: (providers: IProviders, jobParameters?: IReposJob) => Promise, + options?: IReposJobOptions + ) => { + return runJob( + async function (jobParameters: IReposJob) { + return (await script(jobParameters.providers, jobParameters)) || {}; + }, + Object.assign({ enableAllGitHubApps: true }, options || {}) + ); + }, +}; + +export default job; diff --git a/jobs/cleanupBlobCache/task.ts b/jobs/cleanupBlobCache.ts similarity index 81% rename from jobs/cleanupBlobCache/task.ts rename to jobs/cleanupBlobCache.ts index cccf1bd0d..295326351 100644 --- a/jobs/cleanupBlobCache/task.ts +++ b/jobs/cleanupBlobCache.ts @@ -3,10 +3,15 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import BlobCache from '../../lib/caching/blob'; -import { IReposJob } from '../../interfaces'; +// Job 17: Cleanup blob cache -export default async function go({ providers }: IReposJob): Promise { +import BlobCache from '../lib/caching/blob'; +import job from '../job'; +import { IProviders } from '../interfaces'; + +job.runBackgroundJob(cleanup); + +async function cleanup(providers: IProviders): Promise { for (const providerName in providers) { const provider = providers[providerName]; if (provider && provider['expiringBlobCache']) { diff --git a/jobs/cleanupBlobCache/index.ts b/jobs/cleanupBlobCache/index.ts deleted file mode 100644 index c359c26a1..000000000 --- a/jobs/cleanupBlobCache/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -// -// Copyright (c) Microsoft. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. -// - -import Job from './task'; -import app from '../../app'; - -app.runJob(Job); diff --git a/jobs/cleanupInvites/task.ts b/jobs/cleanupInvites.ts similarity index 71% rename from jobs/cleanupInvites/task.ts rename to jobs/cleanupInvites.ts index 6da64f9a4..8d538c7f2 100644 --- a/jobs/cleanupInvites/task.ts +++ b/jobs/cleanupInvites.ts @@ -3,48 +3,50 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import moment from 'moment'; - -import { IReposJob } from '../../interfaces'; +// Job 16: cleanup invites // Organization invitations cleanup: remove any invitations that are older than a // set period of time from the organization. +import { GitHubOrganizationInvite, IProviders } from '../interfaces'; +import job from '../job'; +import { daysInMilliseconds } from '../lib/utils'; + const defaultMaximumInvitationAgeDays = 4; -export default async function cleanup({ providers }: IReposJob): Promise { +job.runBackgroundJob(cleanup, { + timeoutMinutes: 90, + insightsPrefix: 'JobOrganizationInvitationsCleanup', +}); + +async function cleanup(providers: IProviders) { const insights = providers.insights; let maximumInvitationAgeDays = defaultMaximumInvitationAgeDays; const { config, operations } = providers; - if ( - config.github && - config.github.jobs && - config.github.jobs.cleanup && - config.github.jobs.cleanup.maximumInvitationAgeDays - ) { + if (config?.github?.jobs?.cleanup?.maximumInvitationAgeDays) { maximumInvitationAgeDays = config.github.jobs.cleanup.maximumInvitationAgeDays; } - const maximumAgeMoment = moment().subtract(maximumInvitationAgeDays, 'days'); + const maximumAgeDate = new Date(new Date().getTime() - daysInMilliseconds(maximumInvitationAgeDays)); const organizations = operations.getOrganizations(); const removedInvitations = 0; for (const organization of organizations) { - let invitations: any[]; + let invitations: GitHubOrganizationInvite[]; try { invitations = await organization.getMembershipInvitations(); } catch (getInvitationsError) { - insights.trackException({ exception: getInvitationsError }); + insights?.trackException({ exception: getInvitationsError }); console.dir(getInvitationsError); continue; } if (!invitations || invitations.length === 0) { continue; } - const invitationsToRemove = []; + const invitationsToRemove: string[] = []; let emailInvitations = 0; for (let i = 0; i < invitations.length; i++) { const invite = invitations[i]; - const createdAt = moment(invite.created_at); - if (createdAt.isBefore(maximumAgeMoment)) { + const createdAt = new Date(invite.created_at); + if (createdAt < maximumAgeDate) { if (invite.login) { invitationsToRemove.push(invite.login); } else { @@ -52,8 +54,7 @@ export default async function cleanup({ providers }: IReposJob): Promise { console.warn(`An e-mail based invitation to ${invite.email} cannot be automatically canceled`); } const data = { - createdAt: createdAt.format(), - invitedAgo: createdAt.fromNow(), + createdAt: createdAt.toISOString(), login: invite.login, inviter: invite && invite.inviter && invite.inviter.login ? invite.inviter.login : undefined, role: invite.role, @@ -62,7 +63,7 @@ export default async function cleanup({ providers }: IReposJob): Promise { const eventName = invite.login ? 'JobOrganizationInviteCleanupInvitationNeeded' : 'JobOrganizationInviteCleanupInvitationNotUser'; - insights.trackEvent({ + insights?.trackEvent({ name: eventName, properties: data, }); @@ -80,8 +81,8 @@ export default async function cleanup({ providers }: IReposJob): Promise { try { await organization.removeMember(login); } catch (removeError) { - insights.trackException({ exception: removeError }); - insights.trackEvent({ + insights?.trackException({ exception: removeError }); + insights?.trackEvent({ name: 'JobOrganizationInvitationsCleanupInvitationFailed', properties: { login: login, @@ -92,5 +93,5 @@ export default async function cleanup({ providers }: IReposJob): Promise { } } console.log(`Job finishing. Removed ${removedInvitations} expired invitations.`); - insights.trackMetric({ name: 'JobOrganizationInvitationsExpired', value: removedInvitations }); + insights?.trackMetric({ name: 'JobOrganizationInvitationsExpired', value: removedInvitations }); } diff --git a/jobs/cleanupInvites/index.ts b/jobs/cleanupInvites/index.ts deleted file mode 100644 index 7d2e3f56d..000000000 --- a/jobs/cleanupInvites/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -// -// Copyright (c) Microsoft. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. -// - -import Job from './task'; -import app from '../../app'; - -app.runJob(Job, { - timeoutMinutes: 90, - defaultDebugOutput: 'restapi', - insightsPrefix: 'JobOrganizationInvitationsCleanup', -}); diff --git a/jobs/cleanupKeys/index.ts b/jobs/cleanupKeys/index.ts deleted file mode 100644 index 8380b468f..000000000 --- a/jobs/cleanupKeys/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -// -// Copyright (c) Microsoft. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. -// - -import Job from './task'; -import app from '../../app'; - -app.runJob(Job, { timeoutMinutes: 90, defaultDebugOutput: 'restapi', insightsPrefix: 'JobCleanupKeys' }); diff --git a/jobs/cleanupKeys/task.ts b/jobs/cleanupKeys/task.ts deleted file mode 100644 index 4e1d3b019..000000000 --- a/jobs/cleanupKeys/task.ts +++ /dev/null @@ -1,98 +0,0 @@ -// -// Copyright (c) Microsoft. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. -// - -import throat from 'throat'; - -import { IReposJob, IReposJobResult } from '../../interfaces'; -import { sleep } from '../../utils'; -import { IGraphProvider } from '../../lib/graphProvider'; -import { LocalExtensionKey } from '../../entities/localExtensionKey/localExtensionKey'; - -async function lookupCorporateId( - graphProvider: IGraphProvider, - knownUsers: Map, - corporateId: string -): Promise { - const entry = knownUsers.get(corporateId); - if (entry === false) { - return false; - } else if (entry) { - return true; - } - - try { - const userDetails = await graphProvider.getUserById(corporateId); - if (!userDetails || !userDetails.userPrincipalName) { - knownUsers.set(corporateId, false); - return false; - } - knownUsers.set(corporateId, userDetails); - return true; - } catch (otherUserError) { - console.dir(otherUserError); - throw otherUserError; - } -} - -export default async function cleanup({ providers }: IReposJob): Promise { - const graphProvider = providers.graphProvider; - const localExtensionKeyProvider = providers.localExtensionKeyProvider; - const insights = providers.insights; - - console.log('reading all keys'); - const allKeys = await localExtensionKeyProvider.getAllKeys(); - console.log(`read ${allKeys.length}`); - - insights.trackEvent({ name: 'JobCleanupTokensKeysTokens', properties: { tokens: String(allKeys.length) } }); - - let errors = 0; - - let deleted = 0; - let okUserTokens = 0; - - const parallelUsers = 2; - const secondsDelayAfterSuccess = 0.25; - - const knownUsers = new Map(); - - const throttle = throat(parallelUsers); - await Promise.all( - allKeys.map((key: LocalExtensionKey) => - throttle(async () => { - const corporateId = key.corporateId; - const userStatus = await lookupCorporateId(graphProvider, knownUsers, corporateId); - if (!userStatus) { - try { - ++deleted; - console.log(`${deleted}: Deleting key for ${corporateId} that could not be found`); - await localExtensionKeyProvider.delete(key); - } catch (tokenDeleteError) { - --deleted; - console.dir(tokenDeleteError); - ++errors; - insights.trackException({ exception: tokenDeleteError }); - } - } else { - ++okUserTokens; - console.log(`${okUserTokens}: valid`); - } - - await sleep(secondsDelayAfterSuccess * 1000); - }) - ) - ); - - console.log(`deleted: ${deleted}`); - console.log(`okUserTokens: ${okUserTokens}`); - console.log(); - - return { - successProperties: { - deleted, - okUserTokens, - errors, - }, - }; -} diff --git a/jobs/cleanupTeamRequests.ts b/jobs/cleanupTeamRequests.ts new file mode 100644 index 000000000..99bbc59db --- /dev/null +++ b/jobs/cleanupTeamRequests.ts @@ -0,0 +1,65 @@ +// +// Copyright (c) Microsoft. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +// Job 18: cleanup team requests + +// Remove any team requests made by people who are no longer linked. + +import { IProviders } from '../interfaces'; +import job from '../job'; +import { TeamApprovalDecision } from '../routes/org/team/approval'; +import { CreateError } from '../lib/transitional'; + +job.runBackgroundJob(cleanup, { + timeoutMinutes: 5, + insightsPrefix: 'JobTeamRequestsCleanup', +}); + +async function cleanup(providers: IProviders) { + const { approvalProvider, insights, linkProvider } = providers; + if (!approvalProvider) { + throw CreateError.InvalidParameters('No approval provider instance available'); + } + + const linkedCorporateIds = await linkProvider.getAllCorporateIds(); + + let approvals = await approvalProvider.queryAllApprovals(); + approvals = approvals.filter((approval) => approval.active === true && approval.corporateId); + + const orphanApprovals = approvals.filter((approval) => !linkedCorporateIds.includes(approval.corporateId)); + + let removedRequests = 0; + let i = 0; + for (const approval of orphanApprovals) { + ++i; + try { + approval.active = false; + approval.decision = TeamApprovalDecision.Deny; + approval.decisionMessage = 'Requestor not linked'; + await approvalProvider.updateTeamApprovalEntity(approval); + insights?.trackEvent({ + name: 'JobTeamRequestsCleanupApprovalUpdate', + properties: { + approvalId: approval.approvalId, + }, + }); + console.log( + `Denied former linked user request ${approval.approvalId} (${i} of ${orphanApprovals.length})` + ); + ++removedRequests; + } catch (error) { + insights?.trackException({ + exception: error, + properties: { + eventName: 'JobTeamRequestsCleanupApprovalUpdateFailed', + approvalId: approval.approvalId, + }, + }); + console.warn(`Error ${error.message} updating approval ${approval.approvalId}`); + } + } + console.log(`Job finishing. Removed ${removedRequests} requests from former linked users.`); + insights?.trackMetric({ name: 'JobFormerRequestsDenied', value: removedRequests }); +} diff --git a/jobs/cleanupTokens/index.ts b/jobs/cleanupTokens/index.ts deleted file mode 100644 index 2b59c4a5d..000000000 --- a/jobs/cleanupTokens/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -// -// Copyright (c) Microsoft. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. -// - -import Job from './task'; -import app from '../../app'; - -app.runJob(Job, { timeoutMinutes: 90, defaultDebugOutput: 'restapi', insightsPrefix: 'JobCleanupTokens' }); diff --git a/jobs/cleanupTokens/task.ts b/jobs/cleanupTokens/task.ts deleted file mode 100644 index 2bcd443e9..000000000 --- a/jobs/cleanupTokens/task.ts +++ /dev/null @@ -1,142 +0,0 @@ -// -// Copyright (c) Microsoft. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. -// - -import throat from 'throat'; -import { IReposJob, IReposJobResult } from '../../interfaces'; - -// Revoke tokens of users that no longer resolve in the corporate graph and -// delete tokens that have been expired 30 days. - -const expiredTokenDeleteThresholdDays = 30; - -import { PersonalAccessToken } from '../../entities/token/token'; -import { sleep } from '../../utils'; -import { IGraphProvider } from '../../lib/graphProvider'; - -async function lookupCorporateId( - graphProvider: IGraphProvider, - knownUsers: Map, - corporateId: string -): Promise { - const entry = knownUsers.get(corporateId); - if (entry === false) { - return false; - } else if (entry) { - return true; - } - - try { - const userDetails = await graphProvider.getUserById(corporateId); - if (!userDetails || !userDetails.userPrincipalName) { - knownUsers.set(corporateId, false); - return false; - } - knownUsers.set(corporateId, userDetails); - return true; - } catch (otherUserError) { - console.dir(otherUserError); - throw otherUserError; - } -} - -export default async function cleanup({ providers }: IReposJob): Promise { - const insights = providers.insights; - const graphProvider = providers.graphProvider; - const tokenProvider = providers.tokenProvider; - - console.log('reading all tokens'); - const allTokens = await tokenProvider.getAllTokens(); - console.log(`read ${allTokens.length}`); - - insights.trackEvent({ - name: 'JobCleanupTokensReadTokens', - properties: { tokens: String(allTokens.length) }, - }); - - let errors = 0; - - let revokedUnresolved = 0; - let deleted = 0; - let serviceTokens = 0; - let okUserTokens = 0; - - const parallelUsers = 1; - const secondsDelayAfterSuccess = 0.25; - - const now = new Date(); - const monthAgo = new Date(now.getTime() - 1000 * 60 * 60 * 24 * expiredTokenDeleteThresholdDays); - - const knownUsers = new Map(); - - const throttle = throat(parallelUsers); - await Promise.all( - allTokens.map((pat: PersonalAccessToken) => - throttle(async () => { - const isGuidMeansADash = pat.corporateId && pat.corporateId.includes('-'); - let wasUser = false; - if (isGuidMeansADash) { - wasUser = true; - - const userStatus = await lookupCorporateId(graphProvider, knownUsers, pat.corporateId); - if (!userStatus && pat.active !== false) { - pat.active = false; - console.log( - `Revoking key for ${pat.getIdentifier()} - employee ${pat.corporateId} could not be found` - ); - try { - await tokenProvider.updateToken(pat); - ++revokedUnresolved; - } catch (tokenUpdateError) { - console.dir(tokenUpdateError); - ++errors; - insights.trackException({ exception: tokenUpdateError }); - } - } - } else { - ++serviceTokens; - } - - if (pat.isExpired()) { - const dateExpired = pat.expires; - if (dateExpired < monthAgo) { - console.log(`Deleting key for ${pat.getIdentifier()} that expired ${dateExpired}`); - try { - await tokenProvider.deleteToken(pat); - ++deleted; - } catch (tokenDeleteError) { - console.dir(tokenDeleteError); - ++errors; - insights.trackException({ exception: tokenDeleteError }); - } - } else { - console.log( - `Expired key, keeping around ${pat.getIdentifier()} that expired ${dateExpired} for user notification purposes` - ); - } - } else if (wasUser) { - ++okUserTokens; - } - - await sleep(secondsDelayAfterSuccess * 1000); - }) - ) - ); - - console.log(`deleted: ${deleted}`); - console.log(`revokedUnresolved: ${revokedUnresolved}`); - console.log(`okUserTokens: ${okUserTokens}`); - console.log(`serviceTokens: ${serviceTokens}`); - console.log(); - - return { - successProperties: { - deleted, - revokedUnresolved, - okUserTokens, - serviceTokens, - errors, - }, - }; -} diff --git a/jobs/refreshQueryCache/deletedRepositories.ts b/jobs/deletedRepositoriesCache.ts similarity index 91% rename from jobs/refreshQueryCache/deletedRepositories.ts rename to jobs/deletedRepositoriesCache.ts index 46eaac0ac..d68573113 100644 --- a/jobs/refreshQueryCache/deletedRepositories.ts +++ b/jobs/deletedRepositoriesCache.ts @@ -1,14 +1,21 @@ -import app from '../../app'; -import { Organization } from '../../business/organization'; -import { RepositoryCollaboratorCacheEntity } from '../../entities/repositoryCollaboratorCache/repositoryCollaboratorCache'; -import { RepositoryTeamCacheEntity } from '../../entities/repositoryTeamCache/repositoryTeamCache'; -import { IProviders, IReposJob, IReposJobResult } from '../../interfaces'; -import { ErrorHelper } from '../../transitional'; -import { sleep } from '../../utils'; +// +// Copyright (c) Microsoft. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +// Job: Consistency: Deleted repos (7) + +import job from '../job'; +import { Organization } from '../business/organization'; +import { RepositoryCollaboratorCacheEntity } from '../business/entities/repositoryCollaboratorCache/repositoryCollaboratorCache'; +import { RepositoryTeamCacheEntity } from '../business/entities/repositoryTeamCache/repositoryTeamCache'; +import { IProviders, IReposJobResult } from '../interfaces'; +import { ErrorHelper } from '../lib/transitional'; +import { sleep } from '../lib/utils'; const killBitHours = 8; -app.runJob(byUserJob, { +job.runBackgroundJob(byUserJob, { defaultDebugOutput: 'qcuser', timeoutMinutes: 60 * killBitHours, insightsPrefix: 'JobRefreshUserQC', @@ -202,7 +209,7 @@ async function processDeletedRepositories(providers: IProviders): Promise console.log(`removed collaborator repos: ${removedCollaboratorRepositories}`); } -export default async function byUserJob({ providers, args }: IReposJob): Promise { +async function byUserJob(providers: IProviders): Promise { const { config } = providers; if (config?.jobs?.refreshWrites !== true) { console.log('job is currently disabled to avoid metadata refresh/rewrites'); diff --git a/jobs/firehose/task.ts b/jobs/firehose.ts similarity index 89% rename from jobs/firehose/task.ts rename to jobs/firehose.ts index e4acde7e4..6f4db5af7 100644 --- a/jobs/firehose/task.ts +++ b/jobs/firehose.ts @@ -7,24 +7,31 @@ import os from 'os'; import { DateTime } from 'luxon'; -import App from '../../app'; -import ProcessOrganizationWebhook, { IGitHubWebhookProperties } from '../../webhooks/organizationProcessor'; + +import ProcessOrganizationWebhook, { + IGitHubWebhookProperties, +} from '../business/webhooks/organizationProcessor'; import { IGitHubAppInstallation, IGitHubWebhookEnterprise, IProviders, IReposJob, IReposJobResult, -} from '../../interfaces'; -import { sleep } from '../../utils'; -import { IQueueMessage } from '../../lib/queues'; -import getCompanySpecificDeployment from '../../middleware/companySpecificDeployment'; +} from '../interfaces'; +import { sleep } from '../lib/utils'; +import { IQueueMessage } from '../lib/queues'; +import getCompanySpecificDeployment from '../middleware/companySpecificDeployment'; +import job from '../job'; const runningAsOngoingDeployment = true; const hardAbortMs = 1000 * 60 * 5; // 5 minutes -export default async function firehose({ providers, started }: IReposJob): Promise { +job.run(firehose, { + insightsPrefix: 'JobFirehose', +}); + +async function firehose(providers: IProviders, { started }: IReposJob): Promise { const processedEventTypes = {}; const interestingEvents = 0; let processedEvents = 0; @@ -89,28 +96,24 @@ export default async function firehose({ providers, started }: IReposJob): Promi console.log( `Parallelism for this run will be ${parallelism} logical threads, offset by ${sliceDelayPerThread}s` ); - // const insights = app.settings.appInsightsClient; - if (insights) { - insights.trackEvent({ - name: 'JobFirehoseStarted', - properties: { - hostname: os.hostname(), - // queue: serviceBusConfig.queue, - // subscription: serviceBusConfig.subscriptionName, - // messagesInQueue: messagesInQueue.toString(), - // deadLetters: deadLetters.toString(), - }, - }); - // insights.trackMetric({ name: 'FirehoseMessagesInQueue', value: messagesInQueue }); - // insights.trackMetric({ name: 'FirehoseDeadLetters', value: deadLetters }); - } + insights?.trackEvent({ + name: 'JobFirehoseStarted', + properties: { + hostname: os.hostname(), + // queue: serviceBusConfig.queue, + // subscription: serviceBusConfig.subscriptionName, + // messagesInQueue: messagesInQueue.toString(), + // deadLetters: deadLetters.toString(), + }, + }); + // insights.trackMetric({ name: 'FirehoseMessagesInQueue', value: messagesInQueue }); + // insights.trackMetric({ name: 'FirehoseDeadLetters', value: deadLetters }); const threads: Promise[] = []; let delay = 0; for (let i = 0; i < parallelism; i++) { - threads.push(createThread(App, providers, i, delay)); + threads.push(createThread(providers, i, delay)); delay += sliceDelayPerThread; } - const ok = true; await Promise.all(threads); console.warn('Forever execution thread has completed.'); @@ -119,7 +122,6 @@ export default async function firehose({ providers, started }: IReposJob): Promi // -- end of job startup -- async function createThread( - app, providers: IProviders, threadNumber: number, startupDelay: number @@ -136,7 +138,7 @@ export default async function firehose({ providers, started }: IReposJob): Promi await iterate(providers, threadNumber); } } catch (error) { - const insights = app.settings.appInsightsClient; + const insights = providers.insights; insights.trackException({ exception: error }); insights.trackEvent({ name: 'JobFirehoseFatalError', diff --git a/jobs/firehose/index.ts b/jobs/firehose/index.ts deleted file mode 100644 index 9ac3820d6..000000000 --- a/jobs/firehose/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -// -// Copyright (c) Microsoft. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. -// - -import Job from './task'; -import app from '../../app'; - -app.runJob(Job, { - defaultDebugOutput: 'redis,restapi,querycache', - insightsPrefix: 'JobFirehose', - enableAllGitHubApps: true, -}); diff --git a/jobs/permissions.ts b/jobs/permissions.ts new file mode 100644 index 000000000..93031a4fc --- /dev/null +++ b/jobs/permissions.ts @@ -0,0 +1,214 @@ +// +// Copyright (c) Microsoft. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +// Job 15: System Team Permissions + +import { shuffle } from 'lodash'; +import throat from 'throat'; + +import job from '../job'; +import { TeamPermission } from '../business/teamPermission'; +import { GitHubRepositoryPermission, IProviders, IReposJobResult } from '../interfaces'; +import AutomaticTeamsWebhookProcessor from '../business/webhooks/tasks/automaticTeams'; +import { sleep } from '../lib/utils'; +import { ErrorHelper } from '../lib/transitional'; +import { Organization } from '../business'; + +// Permissions processing: visit all repos and make sure that any designated read, write, admin +// teams for the organization are present on every repo. This job is designed to be run relatively +// regularly but is not looking to answer "the truth" - it will use the cache of repos and other +// assets to not abuse GitHub and its API exhaustively. Over time repos will converge to having +// the right permissions. +// +// If a repository is "compliance locked", the system teams are not enforced until the lock is removed. + +const maxParallelism = 3; +const delayBetweenSeconds = 1; + +let updatedPermissions = 0; +let updatedRepos = 0; + +const missingTeams = new Set(); + +job.runBackgroundJob(permissionsRun, { + insightsPrefix: 'JobPermissions', +}); + +async function permissionsRun(providers: IProviders): Promise { + const { config, insights, operations } = providers; + if (config?.jobs?.refreshWrites !== true) { + console.log('job is currently disabled to avoid metadata refresh/rewrites'); + return; + } + + const throttle = throat(maxParallelism); + + const organizations = shuffle(Array.from(operations.organizations.values())); + + await Promise.allSettled( + organizations.map((organization, index) => + throttle(async () => { + return reviewOrganizationSystemTeams(providers, organization, index, organizations.length); + }) + ) + ); + + console.log(`Updated ${updatedPermissions} permissions across ${organizations.length} organizations`); + insights?.trackMetric({ name: 'JobSystemTeamsUpdatedPermissions', value: updatedPermissions }); + + console.log(`Updated ${updatedRepos} repos across ${organizations.length} organizations`); + insights?.trackMetric({ name: 'JobSystemTeamsUpdatedRepos', value: updatedRepos }); + + return {}; +} + +async function reviewOrganizationSystemTeams( + providers: IProviders, + organization: Organization, + index: number, + count: number +) { + const { insights } = providers; + const prefix = `${index}/${count}: ${organization.name}: `; + + console.log(`${prefix} Reviewing permissions for all repos in ${organization.name}...`); + try { + const repos = await organization.getRepositories(); + console.log(`Repos in the ${organization.name} org: ${repos.length}`); + const automaticTeams = new AutomaticTeamsWebhookProcessor(); + for (const repo of repos) { + let thisRepoUpdated = false; + console.log(`${organization.name}/${repo.name}`); + sleep(1000 * delayBetweenSeconds); + const cacheOptions = { + maxAgeSeconds: 10 * 60 /* 10m */, + backgroundRefresh: false, + }; + const { specialTeamIds, specialTeamLevels } = automaticTeams.processOrgSpecialTeams(repo.organization); + let permissions: TeamPermission[] = null; + try { + permissions = await repo.getTeamPermissions(cacheOptions); + } catch (getError) { + if (ErrorHelper.IsNotFound(getError)) { + console.log(`Repo gone: ${repo.organization.name}/${repo.name}`); + } else { + console.log( + `There was a problem getting the permissions for the repo ${repo.name} from ${repo.organization.name}` + ); + console.dir(getError); + } + continue; + } + let shouldSkipEnforcement = false; + const { customizedTeamPermissionsWebhookLogic } = providers; + if (customizedTeamPermissionsWebhookLogic) { + shouldSkipEnforcement = await customizedTeamPermissionsWebhookLogic.shouldSkipEnforcement(repo); + } + const currentPermissions = new Map(); + permissions.forEach((entry) => { + currentPermissions.set(Number(entry.team.id), entry.getAsPermission()); + }); + const teamsToSet = new Set(); + specialTeamIds.forEach((specialTeamId) => { + if (!currentPermissions.has(specialTeamId)) { + teamsToSet.add(specialTeamId); + } else if ( + isAtLeastPermissionLevel( + currentPermissions.get(specialTeamId), + specialTeamLevels.get(specialTeamId) + ) + ) { + // The team permission is already acceptable + } else { + console.log( + `Permission level for ${specialTeamId} is not good enough, expected ${specialTeamLevels.get( + specialTeamId + )} but currently ${currentPermissions.get(specialTeamId)}` + ); + teamsToSet.add(specialTeamId); + } + }); + const setArray = Array.from(teamsToSet.values()); + for (const teamId of setArray) { + const newPermission = specialTeamLevels.get(teamId); + if ( + shouldSkipEnforcement && + (newPermission as GitHubRepositoryPermission) !== GitHubRepositoryPermission.Pull + ) { + console.log( + `should add ${teamId} team with permission ${newPermission} to the repo ${repo.name}, but compliance lock prevents non-read system teams` + ); + insights?.trackEvent({ + name: 'JobSystemTeamsSkipped', + properties: { + org: organization.name, + repo: repo.name, + teamId, + reason: 'compliance lock', + newPermission, + }, + }); + } else { + try { + if (!missingTeams.has(teamId)) { + await repo.setTeamPermission(teamId, newPermission as GitHubRepositoryPermission); + ++updatedPermissions; + thisRepoUpdated = true; + insights?.trackEvent({ + name: 'JobSystemTeamsUpdated', + properties: { + org: organization.name, + repo: repo.name, + teamId, + newPermission, + }, + }); + } + } catch (error) { + if (ErrorHelper.IsNotFound(error)) { + missingTeams.add(teamId); + console.log( + `the team ID ${teamId} could not be found when setting to repo ${repo.name} in org ${organization.name} and should likely be removed from config...` + ); + } else { + console.log(`${repo.name}`); + console.dir(error); + throw error; + } + } + } + } + if (thisRepoUpdated) { + ++updatedRepos; + } + } + console.log(`Finished with repos in ${organization.name} organization`); + } catch (processOrganizationError) { + console.dir(processOrganizationError); + console.log(`moving past ${organization.name} processing due to error...`); + } +} + +function isAtLeastPermissionLevel(value, expected) { + if (value !== 'admin' && value !== 'push' && value !== 'pull') { + throw new Error(`The permission type ${value} is not understood by isAtLeastPermissionLevel`); + } + if (value === expected) { + return true; + } + // Admin always wins + if (value === 'admin') { + return true; + } else if (expected === 'admin') { + return false; + } + if (expected === 'write' && value === expected) { + return true; + } + if (expected === 'read') { + return true; + } + return false; +} diff --git a/jobs/permissions/index.ts b/jobs/permissions/index.ts deleted file mode 100644 index 40b4097e0..000000000 --- a/jobs/permissions/index.ts +++ /dev/null @@ -1,14 +0,0 @@ -// -// Copyright (c) Microsoft. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. -// - -// Job: System Team Permissions - -import Job from './task'; -import app from '../../app'; - -app.runJob(Job, { - insightsPrefix: 'JobPermissions', - defaultDebugOutput: 'cache,restapi', -}); diff --git a/jobs/permissions/task.ts b/jobs/permissions/task.ts deleted file mode 100644 index 311e60711..000000000 --- a/jobs/permissions/task.ts +++ /dev/null @@ -1,155 +0,0 @@ -// -// Copyright (c) Microsoft. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. -// - -// Job: System Team Permissions - -import { shuffle } from 'lodash'; - -import { TeamPermission } from '../../business/teamPermission'; -import { GitHubRepositoryPermission, IReposJob, IReposJobResult } from '../../interfaces'; -import AutomaticTeamsWebhookProcessor from '../../webhooks/tasks/automaticTeams'; -import { sleep } from '../../utils'; -import { ErrorHelper } from '../../transitional'; - -// Permissions processing: visit all repos and make sure that any designated read, write, admin -// teams for the organization are present on every repo. This job is designed to be run relatively -// regularly but is not looking to answer "the truth" - it will use the cache of repos and other -// assets to not abuse GitHub and its API exhaustively. Over time repos will converge to having -// the right permissions. -// -// If a repository is "compliance locked", the system teams are not enforced until the lock is removed. - -const maxParallelism = 1; - -const delayBetweenSeconds = 1; - -export default async function permissionsRun({ providers }: IReposJob): Promise { - const { config, operations } = providers; - if (config?.jobs?.refreshWrites !== true) { - console.log('job is currently disabled to avoid metadata refresh/rewrites'); - return; - } - - for (const organization of shuffle(Array.from(operations.organizations.values()))) { - console.log(`Reviewing permissions for all repos in ${organization.name}...`); - try { - const repos = await organization.getRepositories(); - console.log(`Repos in the ${organization.name} org: ${repos.length}`); - let z = 0; - const automaticTeams = new AutomaticTeamsWebhookProcessor(); - for (const repo of repos) { - console.log(`${repo.organization.name}/${repo.name}`); - sleep(1000 * delayBetweenSeconds); - const cacheOptions = { - maxAgeSeconds: 10 * 60 /* 10m */, - backgroundRefresh: false, - }; - ++z; - if (z % 250 === 1) { - console.log('. ' + z); - } - const { specialTeamIds, specialTeamLevels } = automaticTeams.processOrgSpecialTeams( - repo.organization - ); - let permissions: TeamPermission[] = null; - try { - permissions = await repo.getTeamPermissions(cacheOptions); - } catch (getError) { - if (getError.status == /* loose */ 404) { - console.log(`Repo gone: ${repo.organization.name}/${repo.name}`); - } else { - console.log( - `There was a problem getting the permissions for the repo ${repo.name} from ${repo.organization.name}` - ); - console.dir(getError); - } - continue; - } - let shouldSkipEnforcement = false; - const { customizedTeamPermissionsWebhookLogic } = providers; - if (customizedTeamPermissionsWebhookLogic) { - shouldSkipEnforcement = await customizedTeamPermissionsWebhookLogic.shouldSkipEnforcement(repo); - } - const currentPermissions = new Map(); - permissions.forEach((entry) => { - currentPermissions.set(Number(entry.team.id), entry.permission); - }); - const teamsToSet = new Set(); - specialTeamIds.forEach((specialTeamId) => { - if (!currentPermissions.has(specialTeamId)) { - teamsToSet.add(specialTeamId); - } else if ( - isAtLeastPermissionLevel( - currentPermissions.get(specialTeamId), - specialTeamLevels.get(specialTeamId) - ) - ) { - // The team permission is already acceptable - } else { - console.log( - `Permission level for ${specialTeamId} is not good enough, expected ${specialTeamLevels.get( - specialTeamId - )} but currently ${currentPermissions.get(specialTeamId)}` - ); - teamsToSet.add(specialTeamId); - } - }); - const setArray = Array.from(teamsToSet.values()); - for (const teamId of setArray) { - const newPermission = specialTeamLevels.get(teamId); - if ( - shouldSkipEnforcement && - (newPermission as GitHubRepositoryPermission) !== GitHubRepositoryPermission.Pull - ) { - console.log( - `should add ${teamId} team with permission ${newPermission} to the repo ${repo.name}, but compliance lock prevents non-read system teams` - ); - } else { - try { - await repo.setTeamPermission(teamId, newPermission as GitHubRepositoryPermission); - } catch (error) { - if (ErrorHelper.IsNotFound(error)) { - console.log( - `the team ID ${teamId} could not be found when setting to repo ${repo.name} in org ${organization.name} and should likely be removed from config...` - ); - } else { - console.log(`${repo.name}`); - console.dir(error); - throw error; - } - } - } - } - } - console.log(`Finished with repos in ${organization.name} organization`); - } catch (processOrganizationError) { - console.dir(processOrganizationError); - console.log(`moving past ${organization.name} processing due to error...`); - } - } - return {}; -} - -function isAtLeastPermissionLevel(value, expected) { - if (value !== 'admin' && value !== 'push' && value !== 'pull') { - throw new Error(`The permission type ${value} is not understood by isAtLeastPermissionLevel`); - } - if (value === expected) { - return true; - } - // Admin always wins - if (value === 'admin') { - return true; - } else if (expected === 'admin') { - return false; - } - if (expected === 'write' && value === expected) { - return true; - } - if (expected === 'read') { - return true; - } - return false; -} diff --git a/jobs/refreshQueryCache/task.ts b/jobs/refreshQueryCache.ts similarity index 97% rename from jobs/refreshQueryCache/task.ts rename to jobs/refreshQueryCache.ts index 18baa46e8..c4d589fda 100644 --- a/jobs/refreshQueryCache/task.ts +++ b/jobs/refreshQueryCache.ts @@ -6,7 +6,17 @@ import throat from 'throat'; import { shuffle } from 'lodash'; -import { permissionsObjectToValue } from '../../transitional'; +const killBitHours = 48; + +import job from '../job'; + +job.runBackgroundJob(refreshQueryCache, { + defaultDebugOutput: 'querycache', + timeoutMinutes: 60 * killBitHours, + insightsPrefix: 'JobRefreshQueryCache', +}); + +import { projectCollaboratorPermissionsObjectToGitHubRepositoryPermission } from '../lib/transitional'; import { Collaborator, Operations, @@ -16,9 +26,9 @@ import { Team, TeamMember, TeamPermission, -} from '../../business'; -import { sleep, addArrayToSet } from '../../utils'; -import QueryCache from '../../business/queryCache'; +} from '../business'; +import { sleep, addArrayToSet } from '../lib/utils'; +import QueryCache from '../business/queryCache'; import { IPagedCacheOptions, ICacheOptions, @@ -37,7 +47,8 @@ import { QueryCacheOperation, IReposJob, IReposJobResult, -} from '../../interfaces'; + IProviders, +} from '../interfaces'; interface IConsistencyStats { new: number; @@ -247,7 +258,6 @@ async function refreshOrganization( const repositories = await organization.getRepositories(slowRequestCacheOptions); console.log(`${organizationIndex}: ${repositories.length} repositories in ${organization.name}`); const repoIds = new Set(repositories.map((repo) => repo.id.toString())); - for (let i = 0; i < repositories.length; i++) { try { const repository = repositories[i]; @@ -375,7 +385,7 @@ async function cacheRepositoryTeams( const repositoryId = repository.id.toString(); for (const teamPermission of repoTeamPermissions) { const teamId = teamPermission.team.id.toString(); - const permission = teamPermission.permission; + const permission = teamPermission.getAsPermission(); const isPrivate = repository.private as boolean; const repoName = repository.name as string; ops.push( @@ -610,7 +620,9 @@ async function cacheRepositoryCollaborators( const operations = []; const repositoryId = repository.id.toString(); for (const collaborator of repoCollaborators) { - const permission = permissionsObjectToValue(collaborator.permissions); + const permission = projectCollaboratorPermissionsObjectToGitHubRepositoryPermission( + collaborator.permissions + ); operations.push( await queryCache.addOrUpdateCollaborator( organizationId, @@ -628,7 +640,7 @@ async function cacheRepositoryCollaborators( return operations.filter((real) => real); } -export default async function refresh({ providers, args }: IReposJob): Promise { +async function refreshQueryCache(providers: IProviders, { args }: IReposJob): Promise { const { config } = providers; if (config?.jobs?.refreshWrites !== true) { console.log('job is currently disabled to avoid metadata refresh/rewrites'); diff --git a/jobs/refreshQueryCache/index.ts b/jobs/refreshQueryCache/index.ts deleted file mode 100644 index 714cb72d2..000000000 --- a/jobs/refreshQueryCache/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -// -// Copyright (c) Microsoft. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. -// - -import Job from './task'; -import app from '../../app'; - -const killBitHours = 48; - -app.runJob(Job, { - defaultDebugOutput: 'querycache', - timeoutMinutes: 60 * killBitHours, - insightsPrefix: 'JobRefreshQueryCache', -}); diff --git a/jobs/refreshUsernames/index.ts b/jobs/refreshUsernames.ts similarity index 67% rename from jobs/refreshUsernames/index.ts rename to jobs/refreshUsernames.ts index c36d7da88..275edc1d8 100644 --- a/jobs/refreshUsernames/index.ts +++ b/jobs/refreshUsernames.ts @@ -6,28 +6,29 @@ // Job: Backfill aliases (3) // Job: User attributes hygiene - alias backfills (4) -import app from '../../app'; +import job from '../job'; import throat from 'throat'; import { shuffle } from 'lodash'; -import { sleep } from '../../utils'; -import { IReposJob, IReposJobResult, UnlinkPurpose } from '../../interfaces'; +import { sleep } from '../lib/utils'; +import { IProviders, IReposJobResult, UnlinkPurpose } from '../interfaces'; +import { ErrorHelper } from '../lib/transitional'; -const backfillAliasesOnly = process.env.BACKFILL_ALIASES === '1'; - -app.runJob(refresh, { - defaultDebugOutput: 'cache,restapi', +job.runBackgroundJob(refresh, { insightsPrefix: 'JobRefreshUsernames', }); -async function refresh({ providers }: IReposJob): Promise { +async function refresh(providers: IProviders): Promise { const { config, operations, insights, linkProvider, graphProvider } = providers; if (config?.jobs?.refreshWrites !== true) { console.log('job is currently disabled to avoid metadata refresh/rewrites'); return; } + const backfillAliasesOnly = config.process.get('BACKFILL_ALIASES') === '1'; + const terminateLinksAndMemberships = config.process.get('REFRESH_USERNAMES_TERMINATE_ACCOUNTS') === '1'; + console.log('reading all links'); let allLinks = shuffle(await linkProvider.getAll()); console.log(`READ: ${allLinks.length} links`); @@ -67,6 +68,9 @@ async function refresh({ providers }: IReposJob): Promise { allLinks.map((link) => throttle(async () => { ++i; + if (i % 100 === 0) { + console.log(`${i}/${allLinks.length}; total updates=${updates}, errors=${errors}`); + } // Refresh GitHub username for the ID const id = link.thirdPartyId; @@ -96,7 +100,19 @@ async function refresh({ providers }: IReposJob): Promise { ++updatedAvatars; } } catch (githubError) { - console.dir(githubError); + if (ErrorHelper.IsNotFound(githubError)) { + console.warn( + `Deleted GitHub account, id=${id}, username_was=${link.thirdPartyUsername}; https://api.github.com/users/${link.thirdPartyUsername}` + ); + insights.trackMetric({ name: 'JobRefreshUsernamesMissingGitHubAccounts', value: 1 }); + insights.trackEvent({ + name: 'JobRefreshUsernamesGitHubAccountNotFound', + properties: { githubid: id, error: githubError.message }, + }); + } else { + console.dir(githubError); + } + throw githubError; } try { @@ -129,7 +145,17 @@ async function refresh({ providers }: IReposJob): Promise { } } catch (graphLookupError) { // Ignore graph lookup issues, other jobs handle terminated employees - console.dir(graphLookupError); + if (ErrorHelper.IsNotFound(graphLookupError)) { + console.warn(`Deleted AAD account, id=${id}, username_was=${link.corporateUsername}`); + insights.trackMetric({ name: 'JobRefreshUsernamesMissingCorporateAccounts', value: 1 }); + insights.trackEvent({ + name: 'JobRefreshUsernamesCorporateAccountNotFound', + properties: { githubid: id, error: graphLookupError.message }, + }); + } else { + console.dir(graphLookupError); + } + throw graphLookupError; } if (changed) { @@ -138,28 +164,31 @@ async function refresh({ providers }: IReposJob): Promise { ++updates; } } catch (getDetailsError) { - if (getDetailsError.status == /* loose compare */ '404') { + if (ErrorHelper.IsNotFound(getDetailsError)) { ++notFoundErrors; insights.trackEvent({ name: 'JobRefreshUsernamesNotFound', - properties: { githubid: id, error: getDetailsError.message }, + properties: { githubid: id, aadId: link.corporateId, error: getDetailsError.message }, }); - try { - await operations.terminateLinkAndMemberships(id, { purpose: UnlinkPurpose.Deleted }); - insights.trackEvent({ - name: 'JobRefreshUsernamesUnlinkDelete', - properties: { githubid: id, error: getDetailsError.message }, - }); - } catch (unlinkDeletedAccountError) { - console.dir(unlinkDeletedAccountError); - insights.trackException({ - exception: unlinkDeletedAccountError, - properties: { githubid: id, event: 'JobRefreshUsernamesDeleteError' }, - }); + if (terminateLinksAndMemberships) { + try { + await operations.terminateLinkAndMemberships(id, { purpose: UnlinkPurpose.Deleted }); + insights.trackEvent({ + name: 'JobRefreshUsernamesUnlinkDelete', + properties: { githubid: id, error: getDetailsError.message }, + }); + } catch (unlinkDeletedAccountError) { + console.dir(unlinkDeletedAccountError); + insights.trackException({ + exception: unlinkDeletedAccountError, + properties: { githubid: id, event: 'JobRefreshUsernamesDeleteError' }, + }); + } } } else { console.dir(getDetailsError); ++errors; + insights.trackMetric({ name: 'JobRefreshUsernamesErrors', value: 1 }); insights.trackException({ exception: getDetailsError, properties: { name: 'JobRefreshUsernamesError' }, @@ -188,8 +217,8 @@ async function refresh({ providers }: IReposJob): Promise { console.log(`Updates: ${updates}`); console.log(`GitHub username changes: ${updatedUsernames}`); console.log(`GitHub avatar changes: ${updatedAvatars}`); - console.log(`AAD name changes: ${updatedAadNames}`); - console.log(`AAD username changes: ${updatedAadUpns}`); + console.log(`Corporate name changes: ${updatedAadNames}`); + console.log(`Corporate username changes: ${updatedAadUpns}`); console.log(`Updated corporate mails: ${updatedCorporateMails}`); return { diff --git a/jobs/repositories.ts b/jobs/repositories.ts index 6c2a306cc..f8e86427e 100644 --- a/jobs/repositories.ts +++ b/jobs/repositories.ts @@ -3,28 +3,27 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -// JOB 13: refresh repository data -// This is very similar to the query cache, but using proper Postgres type entities, and -// not being used by the app today. +// Job 13: refresh repository data -// Implementation is initial and not as robust (will refresh everything, even things not touched -// since the last update; limited telemetry.) +// This is very similar to the query cache, but using proper Postgres type entities, and +// not being used by the app today at runtime. Possible optimizations include only +// targeting refreshes based on last-cached times. The act of refreshing these entities +// also helps keep the standard GitHub repository cache up to date. import throat from 'throat'; -import app from '../app'; +import job from '../job'; import { Organization, sortByRepositoryDate } from '../business'; -import { IRepositoryProvider, RepositoryEntity } from '../entities/repository'; -import { IProviders, IReposJob, IReposJobResult } from '../interfaces'; -import { ErrorHelper } from '../transitional'; -import { sleep } from '../utils'; +import { RepositoryEntity, tryGetRepositoryEntity } from '../business/entities/repository'; +import { IProviders, IReposJobResult } from '../interfaces'; +import { sleep } from '../lib/utils'; -const sleepBetweenReposMs = 125; +const sleepBetweenReposMs = 110; const maxParallel = 6; const shouldUpdateCached = true; -async function refreshRepositories({ providers }: IReposJob): Promise { +async function refreshRepositories(providers: IProviders): Promise { const { config, operations } = providers; if (config?.jobs?.refreshWrites !== true) { console.log('job is currently disabled to avoid metadata refresh/rewrites'); @@ -32,7 +31,7 @@ async function refreshRepositories({ providers }: IReposJob): Promise 0 ? ' continues' : ''})`); + } try { let repositoryEntity = await tryGetRepositoryEntity(repositoryProvider, repo.id); if (await repo.isDeleted()) { @@ -73,28 +76,33 @@ async function processOrganization( continue; } const entity = repo.getEntity(); - let update = false; + let updatedFields: string[] = null; + let replace = false; if (!repositoryEntity) { repositoryEntity = new RepositoryEntity(); - setFields(repositoryProvider, repositoryEntity, entity); + setFields(repositoryEntity, entity, true); await repositoryProvider.insert(repositoryEntity); console.log(`${prefix}inserted ${organization.name}/${repositoryEntity.name}`); continue; } else { - setFields(repositoryProvider, repositoryEntity, entity); - // not detecting changes now - update = true; + updatedFields = setFields(repositoryEntity, entity, false /* not new */); + replace = updatedFields?.length > 0; } - if (!update && shouldUpdateCached) { - update = true; + if (updatedFields.length === 0 && shouldUpdateCached) { + replace = true; repositoryEntity.cached = new Date(); } - if (update) { + if (replace) { await repositoryProvider.replace(repositoryEntity); - console.log(`${prefix}Updated all fields for ${organization.name}/${repo.name}`); + updatedFields.length > 0 && + console.log( + `${prefix}Updated ${updatedFields.length} field${updatedFields.length === 1 ? '' : 's'} for ${ + organization.name + }/${repo.name} [${updatedFields.join(', ')}]` + ); } } catch (error) { - console.warn(`${prefix}repo error: ${repo.name} in organization ${organization.name}`); + console.warn(`${prefix}repo error: ${repo.name} in organization ${organization.name}: ${error}`); } await sleep(sleepBetweenReposMs); @@ -106,70 +114,237 @@ async function processOrganization( return {}; } -function setFields(repositoryProvider: IRepositoryProvider, repositoryEntity: RepositoryEntity, entity: any) { - repositoryEntity.repositoryId = entity.id; - repositoryEntity.archived = entity.archived; - repositoryEntity.cached = new Date(); +function setFields(repositoryEntity: RepositoryEntity, entity: any, isNew: boolean) { + const changed: string[] = []; + if ( + (repositoryEntity.repositoryId || entity.id) && + String(repositoryEntity.repositoryId) !== String(entity.id) + ) { + repositoryEntity.repositoryId = parseInt(entity.id, 10); + changed.push('id'); + } + if ((entity.archived || repositoryEntity.archived) && repositoryEntity.archived !== entity.archived) { + repositoryEntity.archived = entity.archived; + changed.push('archived'); + } if (entity.created_at) { - repositoryEntity.createdAt = new Date(entity.created_at); - } - repositoryEntity.defaultBranch = entity.default_branch; - repositoryEntity.description = entity.description; - repositoryEntity.disabled = entity.disabled; - repositoryEntity.fork = entity.fork; - repositoryEntity.forksCount = entity.forks_count; - repositoryEntity.hasDownloads = entity.has_downloads; - repositoryEntity.hasIssues = entity.has_issues; - repositoryEntity.hasPages = entity.has_pages; - repositoryEntity.hasProjects = entity.has_projects; - repositoryEntity.hasWiki = entity.has_wiki; - repositoryEntity.homepage = entity.homepage; - repositoryEntity.language = entity.language; - repositoryEntity.license = entity.license?.spdx_id; - repositoryEntity.fullName = entity.full_name; - repositoryEntity.organizationId = entity.organization?.id; - repositoryEntity.organizationLogin = entity.organization?.login; - repositoryEntity.name = entity.name; - repositoryEntity.networkCount = entity.network_count; - repositoryEntity.openIssuesCount = entity.open_issues_count; - repositoryEntity.organizationId = entity.organization?.id; - repositoryEntity.parentId = entity.parent?.id; - repositoryEntity.parentName = entity.parent?.login; - repositoryEntity.parentOrganizationId = entity.parent?.organization?.id; - repositoryEntity.parentOrganizationName = entity.parent?.organization?.login; - repositoryEntity.private = entity.private; + const createdAt = new Date(entity.created_at); + const currentCreatedAt = repositoryEntity.createdAt ? new Date(repositoryEntity.createdAt) : null; + if (currentCreatedAt && createdAt && currentCreatedAt.toISOString() !== createdAt.toISOString()) { + repositoryEntity.pushedAt = createdAt; + changed.push('created_at'); + } else if (!currentCreatedAt && createdAt) { + repositoryEntity.createdAt = createdAt; + changed.push('created_at'); + } + } + if ( + (entity.default_branch || repositoryEntity.defaultBranch) && + entity.default_branch !== repositoryEntity.defaultBranch + ) { + repositoryEntity.defaultBranch = entity.default_branch; + changed.push('default_branch'); + } + if ( + (entity.description || repositoryEntity.description) && + entity.description !== repositoryEntity.description + ) { + repositoryEntity.description = entity.description; + changed.push('description'); + } + if ((entity.disabled || repositoryEntity.disabled) && entity.disabled !== repositoryEntity.disabled) { + repositoryEntity.disabled = entity.disabled; + changed.push('disabled'); + } + if ((entity.fork || repositoryEntity.fork) && entity.fork !== repositoryEntity.fork) { + repositoryEntity.fork = entity.fork; + changed.push('fork'); + } + if ( + (entity.forks_count || repositoryEntity.forksCount) && + String(entity.forks_count) !== String(repositoryEntity.forksCount) + ) { + repositoryEntity.forksCount = parseInt(entity.forks_count, 10); + changed.push('forks_count'); + } + if ( + (entity.has_downloads || repositoryEntity.hasDownloads) && + entity.has_downloads !== repositoryEntity.hasDownloads + ) { + repositoryEntity.hasDownloads = entity.has_downloads; + changed.push('has_downloads'); + } + if ((entity.has_issues || repositoryEntity.hasIssues) && entity.has_issues !== repositoryEntity.hasIssues) { + repositoryEntity.hasIssues = entity.has_issues; + changed.push('has_issues'); + } + if ((entity.has_pages || repositoryEntity.hasPages) && entity.has_pages !== repositoryEntity.hasPages) { + repositoryEntity.hasPages = entity.has_pages; + changed.push('has_pages'); + } + if ( + (entity.has_projects || repositoryEntity.hasProjects) && + entity.has_projects !== repositoryEntity.hasProjects + ) { + repositoryEntity.hasProjects = entity.has_projects; + changed.push('has_projects'); + } + if ((entity.has_wiki || repositoryEntity.hasWiki) && entity.has_wiki !== repositoryEntity.hasWiki) { + repositoryEntity.hasWiki = entity.has_wiki; + changed.push('has_wiki'); + } + if ((entity.homepage || repositoryEntity.homepage) && entity.homepage !== repositoryEntity.homepage) { + repositoryEntity.homepage = entity.homepage; + changed.push('homepage'); + } + if ((entity.language || repositoryEntity.language) && entity.language !== repositoryEntity.language) { + repositoryEntity.language = entity.language; + changed.push('language'); + } + if (entity.license?.spdx_id !== repositoryEntity.license) { + repositoryEntity.license = entity.license?.spdx_id; + changed.push('license.spdx_id'); + } + if ((entity.full_name || repositoryEntity.fullName) && entity.full_name !== repositoryEntity.fullName) { + repositoryEntity.fullName = entity.full_name; + changed.push('full_name'); + } + if ( + (entity.organization?.id || repositoryEntity.organizationId) && + String(entity.organization?.id) !== String(repositoryEntity.organizationId) + ) { + repositoryEntity.organizationId = parseInt(entity.organization?.id, 10); + changed.push('organization.id'); + } + if (entity.organization?.login !== repositoryEntity.organizationLogin) { + repositoryEntity.organizationLogin = entity.organization?.login; + changed.push('organization.login'); + } + if ((entity.name || repositoryEntity.name) && entity.name !== repositoryEntity.name) { + repositoryEntity.name = entity.name; + changed.push('name'); + } + if ( + (entity.network_count || repositoryEntity.networkCount) && + String(entity.network_count) !== String(repositoryEntity.networkCount) + ) { + repositoryEntity.networkCount = parseInt(entity.network_count, 10); + changed.push('network_count'); + } + if ( + (entity.open_issues_count || repositoryEntity.openIssuesCount) && + String(entity.open_issues_count) !== String(repositoryEntity.openIssuesCount) + ) { + repositoryEntity.openIssuesCount = parseInt(entity.open_issues_count, 10); + changed.push('open_issues_count'); + } + if ( + (entity.parent?.id || repositoryEntity.parentId) && + String(entity.parent?.id) !== String(repositoryEntity.parentId) + ) { + repositoryEntity.parentId = parseInt(entity.parent?.id, 10); + changed.push('parent.id'); + } + if ( + (entity.parent?.login || repositoryEntity.parentName) && + entity.parent?.login !== repositoryEntity.parentName + ) { + repositoryEntity.parentName = entity.parent?.login; + changed.push('parent.login'); + } + if ( + (entity?.parent?.organization?.id || repositoryEntity.parentOrganizationId) && + String(entity?.parent?.organization?.id) !== String(repositoryEntity.parentOrganizationId) + ) { + repositoryEntity.parentOrganizationId = parseInt(entity.parent?.organization?.id, 10); + changed.push('parent.organization.id'); + } + if ( + (entity?.parent?.organization?.login || repositoryEntity.parentOrganizationName) && + entity?.parent?.organization?.login !== repositoryEntity.parentOrganizationName + ) { + repositoryEntity.parentOrganizationName = entity.parent?.organization?.login; + changed.push('parent.organization.login'); + } + if ((entity.private || repositoryEntity.private) && entity.private !== repositoryEntity.private) { + repositoryEntity.private = entity.private; + changed.push('private'); + } if (entity.pushed_at) { - repositoryEntity.pushedAt = new Date(entity.pushed_at); + const pushedAt = new Date(entity.pushed_at); + const currentPushedAt = repositoryEntity.pushedAt ? new Date(repositoryEntity.pushedAt) : null; + if (currentPushedAt && pushedAt && currentPushedAt.toISOString() !== pushedAt.toISOString()) { + repositoryEntity.pushedAt = pushedAt; + changed.push('pushed_at'); + } else if (!currentPushedAt && pushedAt) { + repositoryEntity.pushedAt = pushedAt; + changed.push('pushed_at'); + } } - repositoryEntity.size = entity.size; - repositoryEntity.stargazersCount = entity.stargazers_count; - repositoryEntity.subscribersCount = entity.subscribers_count; - repositoryEntity.topics = entity.topics; - if (entity.updated_at) { - repositoryEntity.updatedAt = new Date(entity.updated_at); + if ((entity.size || repositoryEntity.size) && String(entity.size) !== String(repositoryEntity.size)) { + repositoryEntity.size = parseInt(entity.size, 10); + changed.push('size'); } - repositoryEntity.visibility = entity.visibility; - repositoryEntity.watchersCount = entity.watchers_count; - return repositoryEntity; -} - -async function tryGetRepositoryEntity( - repositoryProvider: IRepositoryProvider, - repositoryId: number -): Promise { - try { - const repositoryEntity = await repositoryProvider.get(repositoryId); - return repositoryEntity; - } catch (error) { - if (ErrorHelper.IsNotFound(error)) { - return null; + if ( + (entity.stargazers_count || repositoryEntity.stargazersCount) && + String(entity.stargazers_count) !== String(repositoryEntity.stargazersCount) + ) { + repositoryEntity.stargazersCount = parseInt(entity.stargazers_count, 10); + changed.push('stargazers_count'); + } + if ( + (entity.subscribers_count || repositoryEntity.subscribersCount) && + String(entity.subscribers_count) !== String(repositoryEntity.subscribersCount) + ) { + repositoryEntity.subscribersCount = parseInt(entity.subscribers_count, 10); + changed.push('subscribers_count'); + } + if (entity.topics && !repositoryEntity.topics) { + repositoryEntity.topics = entity.topics; + changed.push('topics'); + } else if (!entity.topics && repositoryEntity.topics) { + repositoryEntity.topics = null; + changed.push('topics'); + } else { + const storedTopics = [...(repositoryEntity.topics || [])].sort(); + const entityTopics = [...(entity.topics || [])].sort(); + if (storedTopics.join(',') !== entityTopics.join(',')) { + repositoryEntity.topics = entity.topics; + changed.push('topics'); } - throw error; } + if (entity.updated_at) { + const updatedAt = new Date(entity.updated_at); + const currentUpdatedAt = repositoryEntity.updatedAt ? new Date(repositoryEntity.updatedAt) : null; + if (currentUpdatedAt && updatedAt && currentUpdatedAt.toISOString() !== updatedAt.toISOString()) { + repositoryEntity.updatedAt = updatedAt; + changed.push('updated_at'); + } else if (!currentUpdatedAt && updatedAt) { + repositoryEntity.updatedAt = updatedAt; + changed.push('updated_at'); + } + } + if ( + (entity.visibility || repositoryEntity.visibility) && + entity.visibility !== repositoryEntity.visibility + ) { + repositoryEntity.visibility = entity.visibility; + changed.push('visibility'); + } + if ( + (entity.watchers_count || repositoryEntity.watchersCount) && + String(entity.watchers_count) !== String(repositoryEntity.watchersCount) + ) { + repositoryEntity.watchersCount = parseInt(entity.watchers_count, 10); + changed.push('watchers_count'); + } + if (changed.length > 0 || isNew) { + repositoryEntity.cached = new Date(); + } + return changed; } -app.runJob(refreshRepositories, { - timeoutMinutes: 320, - defaultDebugOutput: 'restapi', +job.run(refreshRepositories, { + timeoutMinutes: 600, insightsPrefix: 'JobRefreshRepositories', }); diff --git a/lib/caching/blob.ts b/lib/caching/blob.ts index 71c49a90b..7dfeada39 100644 --- a/lib/caching/blob.ts +++ b/lib/caching/blob.ts @@ -14,7 +14,7 @@ import { } from '@azure/storage-blob'; import { ICacheHelper } from '.'; -import { gunzipBuffer, gzipString } from '../../utils'; +import { gunzipBuffer, gzipString } from '../utils'; import Debug from 'debug'; const debug = Debug.debug('cache'); diff --git a/lib/caching/cosmosdb.ts b/lib/caching/cosmosdb.ts index 9eb9eb0b9..4406daa51 100644 --- a/lib/caching/cosmosdb.ts +++ b/lib/caching/cosmosdb.ts @@ -6,8 +6,8 @@ import { ICacheHelper } from '.'; import { CosmosClient, Database, Container } from '@azure/cosmos'; import BlobCache, { IBlobCacheOptions } from './blob'; -import { sleep } from '../../utils'; -import { ErrorHelper, sha256 } from '../../transitional'; +import { sleep } from '../utils'; +import { ErrorHelper, getSafeCosmosResourceKey, sha256 } from '../transitional'; import Debug from 'debug'; const debug = Debug.debug('cache'); @@ -55,7 +55,7 @@ export default class CosmosCache implements ICacheHelper { } private safetyKey(str: string) { - return str.replace(/[%:\\/?#]/g, ''); + return getSafeCosmosResourceKey(str); } private key(key: string) { diff --git a/lib/caching/redis.ts b/lib/caching/redis.ts index 53ed8fa55..0b6b8035e 100644 --- a/lib/caching/redis.ts +++ b/lib/caching/redis.ts @@ -10,8 +10,8 @@ import Debug from 'debug'; const debug = Debug.debug('redis'); const debugCrossOrganization = Debug.debug('redis-cross-org'); -import { ICacheHelper } from '.'; -import { gunzipBuffer, gzipString } from '../../utils'; +import type { ICacheHelper } from '.'; +import { gunzipBuffer, gzipString } from '../utils'; export interface ISetCompressedOptions { minutesToExpire?: number; diff --git a/lib/campaigns.ts b/lib/campaigns.ts index 1d3418cf2..5835611cc 100644 --- a/lib/campaigns.ts +++ b/lib/campaigns.ts @@ -3,6 +3,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +import { getSafeCosmosResourceKey } from './transitional'; import CosmosHelper from './cosmosHelper'; export interface ICampaignUserState { @@ -160,9 +161,9 @@ export class StatefulCampaignProvider implements ICampaignHelper { } private key(corporateId: string, campaignGroupId: string, campaignId?: string) { - return campaignId - ? `${campaignGroupId}-${campaignId}-${corporateId}` - : `${campaignGroupId}-${corporateId}`; + return getSafeCosmosResourceKey( + campaignId ? `${campaignGroupId}-${campaignId}-${corporateId}` : `${campaignGroupId}-${corporateId}` + ); } private baseObject(corporateId: string, campaignGroupId: string, campaignId?: string) { diff --git a/lib/config/painlessConfigAsCode.ts b/lib/config/painlessConfigAsCode.ts index de5aeb95b..998ef8c67 100644 --- a/lib/config/painlessConfigAsCode.ts +++ b/lib/config/painlessConfigAsCode.ts @@ -10,6 +10,7 @@ import path from 'path'; import walkBack from 'walk-back'; import { InnerError, IPainlessConfigGet, IProviderOptions } from '.'; import { processEnvironmentProvider } from './environmentConfigurationResolver'; +import { CreateError } from '../transitional'; const debug = Debug.debug('config'); @@ -70,10 +71,14 @@ function configurePackageEnvironments( try { values = environmentPackage(environment); } catch (problemCalling) { - const asText = problemCalling.toString(); - throw new Error( - `While calling the environment package "${npmName}" for the "${environment}" environment an error was thrown: ${asText}`, - { cause: problemCalling } + const asText = problemCalling.toString() as string; + let suggestion = ''; + if (asText.includes('Unable to require environment') && asText.includes('dist')) { + suggestion = 'Consider deleting and rebuilding the `dist` directory. '; + } + throw CreateError.ServerError( + `${suggestion}While calling the environment package "${npmName}" for the "${environment}" environment an error was thrown: ${asText}`, + problemCalling ); } } else if (typeof environmentPackage === 'object') { diff --git a/lib/cosmosSession/index.ts b/lib/cosmosSession/index.ts index d1eb0a313..a457a8da3 100644 --- a/lib/cosmosSession/index.ts +++ b/lib/cosmosSession/index.ts @@ -7,7 +7,7 @@ import { CosmosClient, Database, Container } from '@azure/cosmos'; import { Store } from 'express-session'; import { IAppSession } from '../../interfaces'; -import { ErrorHelper } from '../../transitional'; +import { ErrorHelper } from '../transitional'; export interface ICosmosSessionProviderOptions { endpoint: string; diff --git a/lib/entityMetadataProvider/entityMetadataProvider.ts b/lib/entityMetadataProvider/entityMetadataProvider.ts index 190943c08..6693c9ff0 100644 --- a/lib/entityMetadataProvider/entityMetadataProvider.ts +++ b/lib/entityMetadataProvider/entityMetadataProvider.ts @@ -3,9 +3,9 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { IEntityMetadata, EntityMetadataType } from './entityMetadata'; -import { IEntityMetadataFixedQuery } from './query'; -import { swapMap } from '../../utils'; +import { type IEntityMetadata, EntityMetadataType } from './entityMetadata'; +import type { IEntityMetadataFixedQuery } from './query'; +import { swapMap } from '../utils'; export enum EntityField { Type = 'entityType', diff --git a/lib/entityMetadataProvider/postgres.ts b/lib/entityMetadataProvider/postgres.ts index 9928d9153..3a274c165 100644 --- a/lib/entityMetadataProvider/postgres.ts +++ b/lib/entityMetadataProvider/postgres.ts @@ -22,7 +22,7 @@ import { MetadataMappingDefinition, MetadataMappingDefinitionBase, } from './declarations'; -import { CreateError } from '../../transitional'; +import { CreateError } from '../transitional'; import { IDictionary } from '../../interfaces'; const MetadataColumnName = 'metadata'; @@ -34,6 +34,12 @@ const MapMetadataPropertiesToFields: any = { entityType: 'entitytype', }; +export enum PostgresColumnTranslations { + None = 'none', + Lowercase = 'lowercase', + LowercaseUnderscores = 'lowercase_underscores', +} + class PostgresMetadataDefinition extends MetadataMappingDefinitionBase { constructor(name: string) { super(name); @@ -194,6 +200,23 @@ class PostgresInternals { } } +function translateName(key: string, translations: PostgresColumnTranslations) { + switch (translations) { + case PostgresColumnTranslations.None: { + return key; + } + case PostgresColumnTranslations.Lowercase: { + return key.toLowerCase(); + } + case PostgresColumnTranslations.LowercaseUnderscores: { + return key.replace(/([A-Z])/g, '_$1').toLowerCase(); + } + default: { + throw CreateError.InvalidParameters(`Invalid column translation type: ${translations}`); + } + } +} + export class PostgresConfiguration { static IdentifyNativeFields(type: EntityMetadataType, fieldNames: string[]) { PostgresInternals.instance(type).nativeFieldNames = fieldNames; @@ -206,11 +229,12 @@ export class PostgresConfiguration { static MapFieldsToColumnNames( type: EntityMetadataType, map: Map, - lowercaseColumnNamesAutomatically?: boolean + columnTranslations: PostgresColumnTranslations = PostgresColumnTranslations.None ) { const dest = PostgresInternals.instance(type).mapFieldsToColumnNames; for (const [key, value] of map.entries()) { - dest.set(key, lowercaseColumnNamesAutomatically ? value.toLowerCase() : value); + const translatedValue = translateName(value, columnTranslations); + dest.set(key, translatedValue); } } @@ -230,7 +254,19 @@ export class PostgresConfiguration { return [fieldName, fieldName]; }) ), - true + PostgresColumnTranslations.Lowercase + ); + } + + static MapFieldsToColumnNamesFromListUnderscoreLowercased(type: EntityMetadataType, fieldNames: string[]) { + PostgresConfiguration.MapFieldsToColumnNames( + type, + new Map( + fieldNames.map((fieldName) => { + return [fieldName, fieldName]; + }) + ), + PostgresColumnTranslations.LowercaseUnderscores ); } @@ -652,18 +688,18 @@ export class PostgresEntityMetadataProvider implements IEntityMetadataProvider { } } - private metadataToRowMetadata(metadata: IEntityMetadata): any { - const shallowClone = Object.assign({}, metadata); - delete shallowClone.entityCreated; - delete shallowClone.entityFieldNames; - delete shallowClone.entityId; - delete shallowClone.entityType; - return shallowClone; - } + // private metadataToRowMetadata(metadata: IEntityMetadata): any { + // const shallowClone = Object.assign({}, metadata); + // delete shallowClone.entityCreated; + // delete shallowClone.entityFieldNames; + // delete shallowClone.entityId; + // delete shallowClone.entityType; + // return shallowClone; + // } - private stripEntityIdentities(type: EntityMetadataType, entity: any) { - return stripEntityIdentities(type, entity); - } + // private stripEntityIdentities(type: EntityMetadataType, entity: any) { + // return stripEntityIdentities(type, entity); + // } private rowToMetadataObject(type: EntityMetadataType, row: any): IEntityMetadata { return rowToMetadataObject(type, row); diff --git a/lib/entityMetadataProvider/query.ts b/lib/entityMetadataProvider/query.ts index c902218e6..6c9af613a 100644 --- a/lib/entityMetadataProvider/query.ts +++ b/lib/entityMetadataProvider/query.ts @@ -3,7 +3,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { IEntityMetadataProvider } from './entityMetadataProvider'; +import type { IEntityMetadataProvider } from './entityMetadataProvider'; import { EntityMetadataType, EntityMetadataBase } from './entityMetadata'; // Newer "entity" implementations have fully decoupled and no longer use this single query type. diff --git a/lib/entityMetadataProvider/table.ts b/lib/entityMetadataProvider/table.ts index 4ec002995..5980472ec 100644 --- a/lib/entityMetadataProvider/table.ts +++ b/lib/entityMetadataProvider/table.ts @@ -50,7 +50,7 @@ import { MetadataMappingDefinitionBase, } from './declarations'; import { encryptTableEntity, decryptTableEntity, ITableEncryptionOperationOptions } from './tableEncryption'; -import { CreateError, ErrorHelper } from '../../transitional'; +import { CreateError, ErrorHelper } from '../transitional'; import { IKeyVaultSecretResolver } from '../keyVaultResolver'; export interface ITableEncryptionOptions { diff --git a/lib/github/appPurposes.ts b/lib/github/appPurposes.ts new file mode 100644 index 000000000..4373de395 --- /dev/null +++ b/lib/github/appPurposes.ts @@ -0,0 +1,277 @@ +// +// Copyright (c) Microsoft. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +import { ExecutionEnvironment } from '../../interfaces'; +import { CreateError } from '../transitional'; + +import Debug from 'debug'; +import { GitHubTokenManager } from './tokenManager'; +import GitHubApplication from '../../business/application'; +import { Operations, OperationsCore } from '../../business'; + +const debug = Debug('github:tokens'); + +export enum AppPurpose { + Data = 'Data', + CustomerFacing = 'CustomerFacing', + Operations = 'Operations', + BackgroundJobs = 'BackgroundJobs', // "secondary" / "default" fallback + Updates = 'Updates', + Security = 'Security', + ActionsData = 'ActionsData', +} + +export interface ICustomAppPurpose { + isCustomAppPurpose: boolean; // basic type check + id: string; + name: string; + getForOrganizationName?(organizationName: string): IGitHubAppConfiguration; + getApplicationConfigurationForInitialization?(): IGitHubAppConfiguration; +} + +export type AppPurposeTypes = AppPurpose | ICustomAppPurpose; + +export type CustomAppPurposeWithGetApplications = ICustomAppPurpose & { + getGitHubAppInstances: () => GitHubApplication[]; +}; + +export abstract class CustomAppPurpose implements ICustomAppPurpose { + get isCustomAppPurpose() { + return true; + } + constructor( + public id: string, + public name: string + ) {} +} + +export class CustomAppPurposeOrganizationVariance extends CustomAppPurpose { + private _appsByAppId = new Map(); + + fallbackIfNotConfiguredOrganizationName = false; + + constructor( + private operations: Operations, + public id: string, + public name: string, + private configurations: IGitHubAppConfiguration[] + ) { + super(id, name); + } + + getForOrganizationName(organizationName: string) { + const configuration = this.configurations.find( + (c) => c.specificOrganizationName.toLowerCase() === organizationName.toLowerCase() + ); + if (!configuration && this.fallbackIfNotConfiguredOrganizationName === false) { + throw CreateError.NotFound(`No configuration found for organization ${organizationName}`); + } + return configuration || this.configurations[0]; + } + + getGitHubAppInstances() { + const uniqueAppIds = new Set(this.configurations.map((c) => c.appId).filter((id) => !!id)); + const appInstances: GitHubApplication[] = []; + for (const appId of uniqueAppIds) { + let instance = this._appsByAppId.get(appId); + if (!instance) { + instance = createGitHubAppInstance( + this.operations, + this.configurations.find((c) => c.appId === appId), + this + ); + this._appsByAppId.set(appId, instance); + } + appInstances.push(instance); + } + return appInstances; + } +} + +export class CustomAppPurposeSingleConfiguration extends CustomAppPurpose { + private _appInstance: GitHubApplication; + + constructor( + private operations: Operations, + public id: string, + public name: string, + private configuration: IGitHubAppConfiguration + ) { + super(id, name); + } + + getApplicationConfigurationForInitialization() { + return this.configuration; + } + + getGitHubAppInstances() { + if (!this._appInstance) { + this._appInstance = createGitHubAppInstance(this.operations, this.configuration, this); + } + return this._appInstance; + } +} + +function createGitHubAppInstance( + operations: Operations, + configuration: IGitHubAppConfiguration, + customPurpose: AppPurposeTypes +) { + const app = new GitHubApplication( + operations, + configuration.appId, + configuration.slug, + configuration.description || configuration.slug, + getAppAuthorizationHeader.bind(this, operations, configuration, customPurpose) + ); + return app; +} + +async function getAppAuthorizationHeader( + operations: Operations, + configuration: IGitHubAppConfiguration, + purpose: AppPurposeTypes +): Promise { + const appId = configuration.appId; + const tokenManager = GitHubTokenManager.TryGetTokenManagerForOperations(operations); + const appTokens = await tokenManager.ensureConfigurationAppInitialized(purpose, configuration); + if (!appTokens) { + CreateError.InvalidParameters(`No app tokens found configured for app ID ${appId} in tokens instance.`); + } + const jwt = await appTokens.getAppAuthenticationToken(); + const value = `bearer ${jwt}`; + return value; +} + +export const DefinedAppPurposes = [ + AppPurpose.Data, + AppPurpose.CustomerFacing, + AppPurpose.Operations, + AppPurpose.BackgroundJobs, + AppPurpose.Updates, + AppPurpose.Security, + AppPurpose.ActionsData, +]; + +// export const GitHubAppPurposesExemptFromAllRepositoriesSelection = [AppPurpose.Onboarding]; + +const appPurposeToConfigurationName = { + [AppPurpose.Data]: 'data', + [AppPurpose.CustomerFacing]: 'ui', + [AppPurpose.Operations]: 'operations', + [AppPurpose.BackgroundJobs]: 'jobs', + [AppPurpose.Updates]: 'updates', + [AppPurpose.Security]: 'security', + [AppPurpose.ActionsData]: 'actions', +}; + +export function getAppPurposeId(purpose: AppPurposeTypes) { + if (!purpose) { + return 'n/a'; + } + if ((purpose as ICustomAppPurpose).isCustomAppPurpose === true) { + return (purpose as ICustomAppPurpose).id; + } + const asPurpose = purpose as AppPurpose; + const id = appPurposeToConfigurationName[asPurpose]; + if (!id) { + throw new Error(`No configuration name for purpose ${asPurpose}`); + } + return id; +} + +export function tryGetAppPurposeAppConfiguration(purpose: AppPurposeTypes, organizationName: string) { + if ( + (purpose as ICustomAppPurpose).isCustomAppPurpose === true && + (purpose as ICustomAppPurpose).getForOrganizationName + ) { + return (purpose as ICustomAppPurpose).getForOrganizationName(organizationName); + } +} + +export function tryGetAppPurposeGitHubAppInstances(purpose: AppPurposeTypes) { + if ( + (purpose as ICustomAppPurpose).isCustomAppPurpose === true && + (purpose as CustomAppPurposeWithGetApplications).getGitHubAppInstances + ) { + return (purpose as CustomAppPurposeWithGetApplications).getGitHubAppInstances(); + } + const operations = GitHubAppPurposes.GetOperationsInstanceForBuiltInPurposes(); + const tokenManager = GitHubTokenManager.TryGetTokenManagerForOperations(operations); + const appTokens = tokenManager.getAppForPurpose(purpose); + if (!appTokens) { + throw CreateError.InvalidParameters(`No app tokens found configured for purpose ${purpose}`); + } + const appId = appTokens.appId; + if (!appId) { + throw CreateError.InvalidParameters(`No app ID found configured for purpose ${purpose}`); + } + return [operations.getApplicationById(appId)]; +} + +export class GitHubAppPurposes { + private _operations: Operations; + private static _instance: GitHubAppPurposes = new GitHubAppPurposes(); + + static get AllAvailableAppPurposes() { + debug(`Retrieving all available purposes (${this._instance._purposes.length})`); + return this._instance._purposes; + } + + static RegisterOperationsInstanceForBuiltInPurposes(operations: Operations) { + this._instance._operations = operations; + } + + static GetOperationsInstanceForBuiltInPurposes() { + return this._instance._operations; + } + + static RegisterCustomPurpose(purpose: ICustomAppPurpose) { + debug(`Registering custom purpose ${purpose.id} (${purpose.name})`); + if (purpose.isCustomAppPurpose !== true) { + throw new Error('Purpose must have `isCustomAppPurpose` set to true'); + } + if ( + (this._instance._purposes as ICustomAppPurpose[]) + .filter((p) => (p as ICustomAppPurpose)?.isCustomAppPurpose === true) + .find((p) => p.id === purpose.id) + ) { + throw new Error(`Purpose with ID ${purpose.id} already registered`); + } + this._instance._purposes.push(purpose); + } + + private _purposes: AppPurposeTypes[]; + + constructor() { + this._purposes = [...DefinedAppPurposes]; + } +} + +export enum GitHubAppAuthenticationType { + ForceSpecificInstallation = 'force', + BestAvailable = 'best', +} + +export interface IGitHubAppConfiguration { + clientId?: string; + clientSecret?: string; + appId?: number; + appKey?: string; + appKeyFile?: string; + webhookSecret?: string; + slug?: string; + description?: string; + baseUrl?: string; + + specificOrganizationName?: string; +} + +export interface IGitHubAppsOptions { + operations: OperationsCore; + // app: IReposApplication; + configurations: Map; + executionEnvironment: ExecutionEnvironment; +} diff --git a/business/githubApps/appTokens.ts b/lib/github/appTokens.ts similarity index 76% rename from business/githubApps/appTokens.ts rename to lib/github/appTokens.ts index 7811be3c4..6e75bf780 100644 --- a/business/githubApps/appTokens.ts +++ b/lib/github/appTokens.ts @@ -7,10 +7,11 @@ import { request } from '@octokit/request'; import { createAppAuth, InstallationAccessTokenAuthentication } from '@octokit/auth-app'; import { AppAuthentication, AuthInterface } from '@octokit/auth-app/dist-types/types'; -import { AppPurposeTypes, ICustomAppPurpose } from '.'; -import { IAuthorizationHeaderValue } from '../../interfaces'; +import { AppPurposeTypes, ICustomAppPurpose } from './appPurposes'; +import { AuthorizationHeaderValue } from '../../interfaces'; import Debug from 'debug'; +import { CreateError } from '../transitional'; const debug = Debug('github:tokens'); interface IInstallationToken { @@ -27,6 +28,45 @@ interface IInstallationToken { const InstallationTokenLifetimeMilliseconds = 1000 * 60 * 60; const ValidityOffsetAfterNowMilliseconds = 1000 * 120; // how long to require validity in the future +export enum GitHubTokenType { + PersonalAccessToken = 'ghp', + OAuthAccessToken = 'gho', + UserToServerToken = 'ghu', + ServerToServerToken = 'ghs', + RefreshToken = 'ghr', + FineGrainedPersonalAccessToken = 'github_pat', +} + +export const GitHubTokenTypes = [ + GitHubTokenType.PersonalAccessToken, + GitHubTokenType.OAuthAccessToken, + GitHubTokenType.UserToServerToken, + GitHubTokenType.ServerToServerToken, + GitHubTokenType.RefreshToken, + GitHubTokenType.FineGrainedPersonalAccessToken, +]; + +export function getGitHubTokenTypeFromValue(value: string | AuthorizationHeaderValue): GitHubTokenType { + if (!value) { + throw CreateError.ParameterRequired('value'); + } + if (typeof value === 'object') { + value = value.value; + } else if (typeof value !== 'string') { + throw CreateError.InvalidParameters('value must be a string or AuthorizationHeaderValue'); + } + if (!value.startsWith('token ')) { + throw CreateError.InvalidParameters('value must start with "token "'); + } + const tokenValue = value.substr(6); + for (const tokenType of GitHubTokenTypes) { + if (tokenValue.startsWith(tokenType)) { + return tokenType; + } + } + throw CreateError.InvalidParameters('value does not appear to be a GitHub token'); +} + export class GitHubAppTokens { #privateKey: string; private _appId: number; @@ -39,23 +79,25 @@ export class GitHubAppTokens { static CreateFromBase64EncodedFileString( purpose: AppPurposeTypes, + slug: string, friendlyName: string, applicationId: number, fileContents: string, baseUrl?: string ): GitHubAppTokens { const keyContents = Buffer.from(fileContents, 'base64').toString('utf8').replace(/\r\n/g, '\n'); - return new GitHubAppTokens(purpose, friendlyName, applicationId, keyContents, baseUrl); + return new GitHubAppTokens(purpose, slug, friendlyName, applicationId, keyContents, baseUrl); } static CreateFromString( purpose: AppPurposeTypes, + slug: string, friendlyName: string, applicationId: number, value: string, baseUrl?: string ): GitHubAppTokens { - return new GitHubAppTokens(purpose, friendlyName, applicationId, value, baseUrl); + return new GitHubAppTokens(purpose, slug, friendlyName, applicationId, value, baseUrl); } get appId() { @@ -68,6 +110,7 @@ export class GitHubAppTokens { constructor( purpose: AppPurposeTypes, + public slug: string, public friendlyName: string, appId: number, privateKey: string, @@ -114,7 +157,7 @@ export class GitHubAppTokens { async getInstallationToken( installationId: number, organizationName: string - ): Promise { + ): Promise { const now = new Date(); const requiredValidityPeriod = new Date(now.getTime() + ValidityOffsetAfterNowMilliseconds); const latestToken = this.getLatestValidToken(installationId, requiredValidityPeriod); @@ -213,8 +256,12 @@ function sortByLatestToken(a: IInstallationToken, b: IInstallationToken) { function tokenValidFilter(timeTokenMustBeValid: Date, token: IInstallationToken) { const isValid = token.expires > timeTokenMustBeValid; if (!isValid) { + const header = token.headerValue.substr(6); + const subset = (header.length > 12 ? header.substr(0, 8) : '') + '*'.repeat(4); console.log( - `invalid or expired token being removed: expires=${token.expires} install_id=${token.installationId} org=${token.organizationName}` + `token expired: redacted=${subset}, expires=${token.expires.toISOString()}, install_id=${ + token.installationId + }, org=${token.organizationName}` ); return false; } diff --git a/lib/github/collections.ts b/lib/github/collections.ts index cb18c7215..228ac649c 100644 --- a/lib/github/collections.ts +++ b/lib/github/collections.ts @@ -9,20 +9,30 @@ import Debug from 'debug'; const debug = Debug.debug('restapi'); import cost from './cost'; -import { IRestResponse, flattenData } from './core'; +import { RestResponse, flattenData } from './core'; import { CompositeApiContext, CompositeIntelligentEngine } from './composite'; import { Collaborator } from '../../business/collaborator'; import { Team } from '../../business/team'; -import { IPagedCacheOptions, IGetAuthorizationHeader, IDictionary } from '../../interfaces'; +import { IPagedCacheOptions, GetAuthorizationHeader, IDictionary } from '../../interfaces'; import { RestLibrary } from '.'; -import { sleep } from '../../utils'; +import { sleep } from '../utils'; import GitHubApplication from '../../business/application'; import { RepositoryPrimaryProperties } from '../../business/primaryProperties'; +import { RepositoryInvitation } from '../../business/repositoryInvitation'; export interface IGetAppInstallationsParameters { app_id: string; } +type WithPage = T & { page?: number }; + +type WithOctokitRequest = T & { octokitRequest?: string }; + +export type CollectionCopilotSeatsOptions = { + org: string; + per_page?: number; +}; + export enum GitHubPullRequestState { Open = 'open', Closed = 'closed', @@ -51,23 +61,45 @@ export interface IListPullsParameters { direction?: GitHubSortDirection; } +const mostBasicAccountProperties = ['id', 'login', 'avatar_url']; + const branchDetailsToCopy = ['name', 'commit', 'protected']; const repoDetailsToCopy = RepositoryPrimaryProperties; const teamDetailsToCopy = Team.PrimaryProperties; const memberDetailsToCopy = Collaborator.PrimaryProperties; const appInstallDetailsToCopy = GitHubApplication.PrimaryInstallationProperties; const contributorsDetailsToCopy = [...Collaborator.PrimaryProperties, 'contributions']; +const repoInviteDetailsToCopy = RepositoryInvitation.PrimaryProperties; -const teamPermissionsToCopy = [ - 'id', +type SubReducerProperties = Record; + +type WithSubPropertyReducer = any[] & { subPropertiesToReduce?: SubReducerProperties }; + +const copilotSeatPropertiesToCopy: WithSubPropertyReducer = [ + 'created_at', + 'updated_at', + 'last_activity_at', + 'last_activity_editor', + 'assignee', // id, login; mostBasicAccountProperties +]; +copilotSeatPropertiesToCopy.subPropertiesToReduce = { + assignee: mostBasicAccountProperties, +}; + +const teamPermissionsToCopyForRepository = [ 'name', + 'id', 'slug', 'description', - 'members_count', - 'repos_count', + // 'members_count', + // 'repos_count', 'privacy', - 'permission', + // 'notification_setting', + 'permission', // custom role name at times + 'permissions', // array of booleans for admin, maintain, push, triage, pull + 'parent', // large object for a parent team, if present ]; + const teamRepoPermissionsToCopy = [ 'id', 'name', @@ -76,7 +108,9 @@ const teamRepoPermissionsToCopy = [ 'private', 'fork', 'permissions', + 'role_name', ]; + const pullDetailsToCopy = [ 'id', 'number', @@ -123,8 +157,56 @@ export class RestCollections { this.githubCall = githubCall; } + collectAllPages( + token: string | GetAuthorizationHeader, + collectionCacheKey: string, + octokitApiName: string, + parameters: ParametersType, + cacheOptions: IPagedCacheOptions, + fieldNamesToKeep?: string[] | WithSubPropertyReducer, + arrayReducePropertyName?: string + ): Promise { + return this.generalizedCollectionWithFilter( + collectionCacheKey, + octokitApiName, + fieldNamesToKeep, + token, + parameters, + cacheOptions, + arrayReducePropertyName + ); + } + + collectAllPagesViaHttpGet( + token: string | GetAuthorizationHeader, + collectionCacheKey: string, + getRestUrl: string, + parameters: ParametersType, + cacheOptions: IPagedCacheOptions, + fieldNamesToKeep?: string[] | WithSubPropertyReducer, + arrayReducePropertyName?: string + ): Promise { + const expandedOptions: WithOctokitRequest = Object.assign( + { + octokitRequest: getRestUrl.startsWith('GET ') ? getRestUrl.substr(4) : getRestUrl, + }, + parameters + ); + return this.collectAllPages( + token, + collectionCacheKey, + 'request', + expandedOptions, + cacheOptions, + fieldNamesToKeep, + arrayReducePropertyName + ); + } + + // --- + getOrgRepos( - token: string | IGetAuthorizationHeader, + token: string | GetAuthorizationHeader, options, cacheOptions: IPagedCacheOptions ): Promise { @@ -139,7 +221,7 @@ export class RestCollections { } getOrgTeams( - token: string | IGetAuthorizationHeader, + token: string | GetAuthorizationHeader, options, cacheOptions: IPagedCacheOptions ): Promise { @@ -154,7 +236,7 @@ export class RestCollections { } getTeamChildTeams( - token: string | IGetAuthorizationHeader, + token: string | GetAuthorizationHeader, options, cacheOptions: IPagedCacheOptions ): Promise { @@ -169,7 +251,7 @@ export class RestCollections { } getUserActivity( - token: string | IGetAuthorizationHeader, + token: string | GetAuthorizationHeader, options, cacheOptions: IPagedCacheOptions ): Promise { @@ -184,7 +266,7 @@ export class RestCollections { } getOrgMembers( - token: string | IGetAuthorizationHeader, + token: string | GetAuthorizationHeader, options, cacheOptions: IPagedCacheOptions ): Promise { @@ -198,8 +280,33 @@ export class RestCollections { ); } + getOrganizationCopilotSeats( + token: string | GetAuthorizationHeader, + options: CollectionCopilotSeatsOptions, + cacheOptions: IPagedCacheOptions + ): Promise { + // technically type CopilotSeatData + const orgName = options.org; + delete options.org; + const params = Object.assign( + { + octokitRequest: `GET /orgs/${orgName}/copilot/billing/seats`, + }, + options + ); + return this.generalizedCollectionWithFilter( + 'orgCopilotSeats', + 'request', + copilotSeatPropertiesToCopy, + token, + params, + cacheOptions, + 'seats' + ); + } + getAppInstallations( - token: string | IGetAuthorizationHeader, + token: string | GetAuthorizationHeader, parameters: IGetAppInstallationsParameters, cacheOptions: IPagedCacheOptions ): Promise { @@ -220,7 +327,7 @@ export class RestCollections { } getRepoIssues( - token: string | IGetAuthorizationHeader, + token: string | GetAuthorizationHeader, options, cacheOptions: IPagedCacheOptions ): Promise { @@ -235,7 +342,7 @@ export class RestCollections { } getRepoProjects( - token: string | IGetAuthorizationHeader, + token: string | GetAuthorizationHeader, options, cacheOptions: IPagedCacheOptions ): Promise { @@ -250,14 +357,14 @@ export class RestCollections { } getRepoTeams( - token: string | IGetAuthorizationHeader, + token: string | GetAuthorizationHeader, options, cacheOptions: IPagedCacheOptions ): Promise { return this.generalizedCollectionWithFilter( 'repoTeamPermissions', 'repos.listTeams', - teamPermissionsToCopy, + teamPermissionsToCopyForRepository, token, options, cacheOptions @@ -265,7 +372,7 @@ export class RestCollections { } getRepoContributors( - token: string | IGetAuthorizationHeader, + token: string | GetAuthorizationHeader, options, cacheOptions: IPagedCacheOptions ): Promise { @@ -280,7 +387,7 @@ export class RestCollections { } getRepoCollaborators( - token: string | IGetAuthorizationHeader, + token: string | GetAuthorizationHeader, options, cacheOptions: IPagedCacheOptions ): Promise { @@ -294,8 +401,23 @@ export class RestCollections { ); } + getRepoInvitations( + token: string | GetAuthorizationHeader, + options, + cacheOptions: IPagedCacheOptions + ): Promise { + return this.generalizedCollectionWithFilter( + 'repoInvitations', + 'repos.listInvitations', + repoInviteDetailsToCopy, + token, + options, + cacheOptions + ); + } + getRepoBranches( - token: string | IGetAuthorizationHeader, + token: string | GetAuthorizationHeader, options, cacheOptions: IPagedCacheOptions ): Promise { @@ -310,7 +432,7 @@ export class RestCollections { } getRepoPullRequests( - token: string | IGetAuthorizationHeader, + token: string | GetAuthorizationHeader, options: IListPullsParameters, cacheOptions: IPagedCacheOptions ): Promise { @@ -325,7 +447,7 @@ export class RestCollections { } getTeamMembers( - token: string | IGetAuthorizationHeader, + token: string | GetAuthorizationHeader, options, cacheOptions: IPagedCacheOptions ): Promise { @@ -340,7 +462,7 @@ export class RestCollections { } getTeamRepos( - token: string | IGetAuthorizationHeader, + token: string | GetAuthorizationHeader, options, cacheOptions: IPagedCacheOptions ): Promise { @@ -354,11 +476,12 @@ export class RestCollections { ); } - private async getGithubCollection( - token: string | IGetAuthorizationHeader, - methodName, - options, - cacheOptions: IPagedCacheOptions + private async getGithubCollection( + token: string | GetAuthorizationHeader, + methodName: string, + options: OptionsType, + cacheOptions: IPagedCacheOptions, + arrayReducePropertyName?: string ): Promise { const hasNextPage = this.libraryContext.hasNextPage; const githubCall = this.githubCall; @@ -368,14 +491,14 @@ export class RestCollections { const requests = []; let pages = 0; let currentPage = 0; - const pageLimit = options.pageLimit || cacheOptions['pageLimit'] || Number.MAX_VALUE; + const pageLimit = (options as any)?.pageLimit || cacheOptions['pageLimit'] || Number.MAX_VALUE; const pageRequestDelay = cacheOptions.pageRequestDelay || null; while (!done) { const method = githubCall; const args = []; const currentToken = typeof token === 'string' ? token : await token(); args.push(currentToken); - const clonedOptions = Object.assign({}, options); + const clonedOptions: WithPage = Object.assign({}, options); if (++currentPage > 1) { clonedOptions.page = currentPage; } @@ -384,6 +507,19 @@ export class RestCollections { let result = null; try { result = await (method as any).apply(null, args); + if ( + arrayReducePropertyName && + result[arrayReducePropertyName] && + Array.isArray(result[arrayReducePropertyName]) + ) { + const originalResultProperties = { + headers: result?.headers, + cost: result?.cost, + }; + result = result[arrayReducePropertyName]; + result.headers = originalResultProperties.headers; + result.cost = originalResultProperties.cost; + } recentResult = result; if (result) { ++pages; @@ -432,17 +568,26 @@ export class RestCollections { return { data, requests }; } - private async getFilteredGithubCollection( - token: string | IGetAuthorizationHeader, - methodName, - options, + private async getFilteredGithubCollection( + token: string | GetAuthorizationHeader, + methodName: string, + options: OptionsType, cacheOptions: IPagedCacheOptions, - propertiesToKeep + propertiesToKeep: string[], + arrayReducePropertyName?: string ): Promise { const keepAll = !propertiesToKeep; + const subReductionProperties = + propertiesToKeep && (propertiesToKeep as WithSubPropertyReducer).subPropertiesToReduce; try { // IRequestWithData - const getCollectionResponse = await this.getGithubCollection(token, methodName, options, cacheOptions); + const getCollectionResponse = await this.getGithubCollection( + token, + methodName, + options, + cacheOptions, + arrayReducePropertyName + ); if (!getCollectionResponse) { throw new Error('No response'); } @@ -462,6 +607,14 @@ export class RestCollections { const r = {}; _.forOwn(doNotModify, (value, key) => { if (keepAll || propertiesToKeep.indexOf(key) >= 0) { + if (subReductionProperties && subReductionProperties[key]) { + const validSubKeys = new Set(subReductionProperties[key]); + for (const subKey of Object.getOwnPropertyNames(value)) { + if (!validSubKeys.has(subKey)) { + delete value[subKey]; + } + } + } r[key] = value; } }); @@ -480,21 +633,23 @@ export class RestCollections { } } - private async getFilteredGithubCollectionWithMetadataAnalysis( - token: string | IGetAuthorizationHeader, - methodName, - options, + private async getFilteredGithubCollectionWithMetadataAnalysis( + token: string | GetAuthorizationHeader, + methodName: string, + options: OptionsType, cacheOptions: IPagedCacheOptions, - propertiesToKeep - ): Promise { - const collectionResults = await this.getFilteredGithubCollection( + propertiesToKeep: string[], + arrayReducePropertyName?: string + ): Promise { + const collectionResults = await this.getFilteredGithubCollection( token, methodName, options, cacheOptions, - propertiesToKeep + propertiesToKeep, + arrayReducePropertyName ); - const results = collectionResults.data as IRestResponse; + const results = collectionResults.data as RestResponse; const requests = collectionResults.requests; const pages = []; let dirty = false; @@ -531,12 +686,12 @@ export class RestCollections { } private generalizedCollectionMethod( - token: string | IGetAuthorizationHeader, + token: string | GetAuthorizationHeader, apiName: string, method, options, cacheOptions: IPagedCacheOptions - ): Promise { + ): Promise { const apiContext = new CompositeApiContext(apiName, method, options); apiContext.maxAgeSeconds = cacheOptions.maxAgeSeconds || 600; apiContext.overrideToken(token); @@ -548,37 +703,47 @@ export class RestCollections { return compositeEngine.execute(apiContext); } - private getCollectionAndFilter( - token: string | IGetAuthorizationHeader, - options, + private getCollectionAndFilter( + token: string | GetAuthorizationHeader, + options: OptionsType, cacheOptions: IPagedCacheOptions, - githubClientMethod, - propertiesToKeep + githubClientMethod: string, + propertiesToKeep: string[], + arrayReducePropertyName?: string ) { const capturedThis = this; - return function (token, options) { - return capturedThis.getFilteredGithubCollectionWithMetadataAnalysis( + return function (token: string | GetAuthorizationHeader, options: OptionsType) { + return capturedThis.getFilteredGithubCollectionWithMetadataAnalysis( token, githubClientMethod, options, cacheOptions, - propertiesToKeep + propertiesToKeep, + arrayReducePropertyName ); }; } - private async generalizedCollectionWithFilter( - name, - githubClientMethod, - propertiesToKeep, - token, - options, - cacheOptions: IPagedCacheOptions - ): Promise { + private async generalizedCollectionWithFilter( + name: string, + githubClientMethod: string, + propertiesToKeep: string[], + token: string | GetAuthorizationHeader, + options: OptionsType, + cacheOptions: IPagedCacheOptions, + arrayReducePropertyName?: string + ): Promise { const rows = await this.generalizedCollectionMethod( token, name, - this.getCollectionAndFilter(token, options, cacheOptions, githubClientMethod, propertiesToKeep), + this.getCollectionAndFilter( + token, + options, + cacheOptions, + githubClientMethod, + propertiesToKeep, + arrayReducePropertyName + ), options, cacheOptions ); diff --git a/lib/github/composite.ts b/lib/github/composite.ts index ab9ae1d6a..21bfa9551 100644 --- a/lib/github/composite.ts +++ b/lib/github/composite.ts @@ -13,16 +13,16 @@ import Debug from 'debug'; const debug = Debug.debug('restapi'); import { - IShouldServeCache, + ShouldServeCache, ApiContext, IntelligentEngine, IApiContextRedisKeys, IApiContextCacheValues, ApiContextType, - IRestMetadata, - IRestResponse, + RestMetadata, + RestResponse, } from './core'; -import { IGetAuthorizationHeader } from '../../interfaces'; +import { GetAuthorizationHeader } from '../../interfaces'; import appPackage from '../../package.json'; @@ -34,7 +34,7 @@ const acceleratedExpirationMinutes = 60; // 1 hour export class CompositeApiContext extends ApiContext { private _apiMethod: any; private _apiTypePrefix: string; - private _token: string | IGetAuthorizationHeader; + private _token: string | GetAuthorizationHeader; private _cacheValues: IApiContextCacheValues; private _redisKeys: IApiContextRedisKeys; @@ -74,11 +74,11 @@ export class CompositeApiContext extends ApiContext { return this._cacheValues; } - get token(): string | IGetAuthorizationHeader { + get token(): string | GetAuthorizationHeader { return this._token; } - overrideToken(token: string | IGetAuthorizationHeader) { + overrideToken(token: string | GetAuthorizationHeader) { this._token = token; } @@ -98,12 +98,12 @@ export class CompositeApiContext extends ApiContext { export class CompositeIntelligentEngine extends IntelligentEngine { withMetadataShouldCacheBeServed( apiContext: ApiContext, - metadata: IRestMetadata - ): boolean | IShouldServeCache { + metadata: RestMetadata + ): boolean | ShouldServeCache { // result can be falsy OR an object; { cache: true, refresh: true } // cache: whether to use the cache, if available // refresh: whether to refresh in the background for a newer value - let shouldServeCache: IShouldServeCache | boolean = false; + let shouldServeCache: ShouldServeCache | boolean = false; const maxAgeSeconds = apiContext.maxAgeSeconds; const updatedIso = metadata ? metadata.updated : null; const refreshingIso = metadata ? metadata.refreshing : null; @@ -173,7 +173,7 @@ export class CompositeIntelligentEngine extends IntelligentEngine { return shouldServeCache; } - withResponseShouldCacheBeServed(apiContext: ApiContext, response: IRestResponse) { + withResponseShouldCacheBeServed(apiContext: ApiContext, response: RestResponse) { if (typeof response === 'function') { throw new Error('The response must not be a function'); } @@ -198,16 +198,16 @@ export class CompositeIntelligentEngine extends IntelligentEngine { return shouldUseCache; } - optionalStripResponse(apiContext: ApiContext, response: IRestResponse): IRestResponse { + optionalStripResponse(apiContext: ApiContext, response: RestResponse): RestResponse { // Composite does not strip any results further before caching return response; } - withResponseUpdateMetadata(apiContext: ApiContext, response: IRestResponse) { + withResponseUpdateMetadata(apiContext: ApiContext, response: RestResponse) { return response; } - reduceMetadataToCacheFromResponse(apiContext: ApiContext, response: IRestResponse) { + reduceMetadataToCacheFromResponse(apiContext: ApiContext, response: RestResponse) { // No reduction for object type metadata. // Store the app version in case it is needed for a future // schema update or cache invalidation @@ -217,7 +217,7 @@ export class CompositeIntelligentEngine extends IntelligentEngine { } } - async callApi(apiContext: CompositeApiContext): Promise { + async callApi(apiContext: CompositeApiContext): Promise { const args = []; const apiMethod = apiContext.apiMethod; if (apiContext.token) { @@ -232,10 +232,10 @@ export class CompositeIntelligentEngine extends IntelligentEngine { } catch (applyError) { throw applyError; } - return unknown as IRestResponse; + return unknown as RestResponse; } - getResponseMetadata(apiContext: CompositeApiContext, response: IRestResponse): IRestMetadata { + getResponseMetadata(apiContext: CompositeApiContext, response: RestResponse): RestMetadata { const headers = response.headers || {}; const calledTime = apiContext.calledTime ? apiContext.calledTime.toISOString() : new Date().toISOString(); headers.updated = calledTime; @@ -251,7 +251,7 @@ export class CompositeIntelligentEngine extends IntelligentEngine { return headers; } - processMetadataBeforeCall(apiContext: CompositeApiContext, metadata: IRestMetadata) { + processMetadataBeforeCall(apiContext: CompositeApiContext, metadata: RestMetadata) { if (metadata && !metadata.av) { // Old version of metadata, no package version, which is required for all composite metadata now metadata = undefined; diff --git a/lib/github/core.ts b/lib/github/core.ts index 94acc778f..2dbc8cd43 100644 --- a/lib/github/core.ts +++ b/lib/github/core.ts @@ -8,11 +8,11 @@ import { randomUUID } from 'crypto'; import moment from 'moment'; import { RestLibrary } from '.'; -import { IAuthorizationHeaderValue } from '../../interfaces'; -import { sleep } from '../../utils'; +import { AuthorizationHeaderValue } from '../../interfaces'; +import { sleep } from '../utils'; import cost from './cost'; -import { ErrorHelper } from '../../transitional'; +import { ErrorHelper } from '../transitional'; import Debug from 'debug'; const debug = Debug.debug('restapi'); @@ -43,7 +43,7 @@ const headerKeysWanted = [ 'x-ratelimit-used', ]; -export interface IInterestingHeaders { +export type GitHubRestInterestingHeaders = { etag?: string; ['last-modified']?: string; ['x-github-request-id']?: string; @@ -51,9 +51,13 @@ export interface IInterestingHeaders { ['x-ratelimit-remaining']?: string; ['x-ratelimit-reset']?: string; ['x-ratelimit-used']?: string; -} +}; + +export type WithGitHubRestHeaders = T & { + headers?: GitHubRestInterestingHeaders; +}; -export interface ISpecializedCollectionHeaders { +export type GitHubRestSpecializedCollectionHeaders = { // really, these are the metadata fields, no headers at all... dirty?: boolean; pages?: string[]; @@ -63,34 +67,34 @@ export interface ISpecializedCollectionHeaders { updated?: string; changed?: string; ['last-modified']?: string; -} +}; -export interface IRestMetadata { +export type RestMetadata = { etag?: string; av?: string; updated?: string; changed?: string; refreshing?: string; - headers?: ISpecializedCollectionHeaders; // IDictionary; + headers?: GitHubRestSpecializedCollectionHeaders; // IDictionary; status?: number; pages?: string[]; -} +}; -export interface IRestResponse { - headers?: ISpecializedCollectionHeaders; +export type RestResponse = { + headers?: GitHubRestSpecializedCollectionHeaders; status?: number; data: unknown; cost?: unknown; notModified?: boolean; -} +}; -export interface IIntelligentCacheResponseArray extends Array, IRestResponse {} +export interface IIntelligentCacheResponseArray extends Array, RestResponse {} -export interface IShouldServeCache { +export type ShouldServeCache = { cache?: boolean; remaining?: string; refresh?: boolean; -} +}; export abstract class ApiContext { private _log: string[]; @@ -99,7 +103,7 @@ export abstract class ApiContext { libraryContext: RestLibrary; etag?: string; - tokenSource: IAuthorizationHeaderValue; + tokenSource: AuthorizationHeaderValue; abstract get apiTypePrefix(): string; abstract get cacheValues(): IApiContextCacheValues; @@ -122,7 +126,10 @@ export abstract class ApiContext { return this._cost; } - constructor(public api: any, public options: any) { + constructor( + public api: any, + public options: any + ) { this._log = []; if (!this._calledTime) { this._calledTime = new Date(); @@ -135,7 +142,7 @@ export abstract class ApiContext { maxAgeSeconds?: number; backgroundRefresh?: boolean; - metadata?: IRestMetadata; + metadata?: RestMetadata; generatedRefreshId?: string; } @@ -164,23 +171,23 @@ export abstract class IntelligentEngine { } // was in api context: - abstract processMetadataBeforeCall(apiContext: ApiContext, metadata: IRestMetadata): IRestMetadata; - abstract callApi(apiContext: ApiContext, optionalMessage?: string): Promise; - abstract withResponseUpdateMetadata(apiContext: ApiContext, response: IRestResponse): IRestResponse; + abstract processMetadataBeforeCall(apiContext: ApiContext, metadata: RestMetadata): RestMetadata; + abstract callApi(apiContext: ApiContext, optionalMessage?: string): Promise; + abstract withResponseUpdateMetadata(apiContext: ApiContext, response: RestResponse): RestResponse; abstract withResponseShouldCacheBeServed( apiContext: ApiContext, - response: IRestResponse - ): boolean | IShouldServeCache; + response: RestResponse + ): boolean | ShouldServeCache; abstract withMetadataShouldCacheBeServed( apiContext: ApiContext, - metadata: IRestMetadata - ): boolean | IShouldServeCache; - abstract reduceMetadataToCacheFromResponse(apiContext: ApiContext, response: IRestResponse): IRestMetadata; - abstract getResponseMetadata(apiContext: ApiContext, response: IRestResponse): IRestMetadata; - abstract optionalStripResponse(apiContext: ApiContext, response: IRestResponse): IRestResponse; + metadata: RestMetadata + ): boolean | ShouldServeCache; + abstract reduceMetadataToCacheFromResponse(apiContext: ApiContext, response: RestResponse): RestMetadata; + abstract getResponseMetadata(apiContext: ApiContext, response: RestResponse): RestMetadata; + abstract optionalStripResponse(apiContext: ApiContext, response: RestResponse): RestResponse; - protected async cacheResponseAsync(apiContext: ApiContext, response: IRestResponse) { + protected async cacheResponseAsync(apiContext: ApiContext, response: RestResponse) { const backgroundAsyncWork = async () => { try { await this.storeResult(apiContext, response); @@ -197,7 +204,7 @@ export abstract class IntelligentEngine { return this.finalizeResult(apiContext, response); } - protected finalizeResult(apiContext: ApiContext, response: IRestResponse): IRestResponse { + protected finalizeResult(apiContext: ApiContext, response: RestResponse): RestResponse { if (!response || !response.data) { // This was a warning in the past, but to try and improve the underlying library, this should be an error if (response.headers.av) { @@ -218,11 +225,9 @@ export abstract class IntelligentEngine { return response; } - protected async tryGetCachedResult(apiContext: ApiContext): Promise { + protected async tryGetCachedResult(apiContext: ApiContext): Promise { const key = this.redisKeyBodyVersion(apiContext); - const response = (await apiContext.libraryContext.cacheProvider.getObjectCompressed( - key - )) as IRestResponse; + const response = (await apiContext.libraryContext.cacheProvider.getObjectCompressed(key)) as RestResponse; this.recordRedisCost(apiContext, 'get', response); return response; } @@ -230,8 +235,8 @@ export abstract class IntelligentEngine { protected async getCachedResult( apiContext: ApiContext, optionalCacheDecisions?, - notModifiedHeaders?: IInterestingHeaders - ): Promise { + notModifiedHeaders?: GitHubRestInterestingHeaders + ): Promise { const result = await this.tryGetCachedResult(apiContext); if (result && result.data) { // use the context metadata over any headers in the stored response, + any headers from 304 @@ -310,7 +315,7 @@ export abstract class IntelligentEngine { protected async reduceObjectExpirationWindow( apiContext: ApiContext, - response: IRestResponse + response: RestResponse ): Promise { if (!apiContext.etag || (apiContext.etag && apiContext.etag === response.headers.etag)) { return; @@ -345,7 +350,7 @@ export abstract class IntelligentEngine { this.recordRedisCost(apiContext, 'expire', cost); } - protected async storeMetadata(apiContext: ApiContext, response: IRestResponse): Promise { + protected async storeMetadata(apiContext: ApiContext, response: RestResponse): Promise { const reducedMetadata = this.reduceMetadataToCacheFromResponse(apiContext, response); const cost = await apiContext.libraryContext.cacheProvider.setObjectWithExpire( apiContext.redisKey.metadata, @@ -355,7 +360,7 @@ export abstract class IntelligentEngine { this.recordRedisCost(apiContext, 'set', cost); } - protected async storeResult(apiContext: ApiContext, response: IRestResponse): Promise { + protected async storeResult(apiContext: ApiContext, response: RestResponse): Promise { let key = null; try { key = this.redisKeyBodyVersion(apiContext, response.headers.etag); @@ -399,23 +404,23 @@ export abstract class IntelligentEngine { return object; } - public async execute(apiContext: ApiContext): Promise { + public async execute(apiContext: ApiContext): Promise { let metadata = await this.getCachedMetadata(apiContext); metadata = this.processMetadataBeforeCall(apiContext, metadata); - const shouldCacheBeServedImmediately: boolean | IShouldServeCache = this.withMetadataShouldCacheBeServed( + const shouldCacheBeServedImmediately: boolean | ShouldServeCache = this.withMetadataShouldCacheBeServed( apiContext, metadata ); const displayKey = apiContext.redisKey ? apiContext.redisKey.root + ' ' : ''; if ( shouldCacheBeServedImmediately === true || - (shouldCacheBeServedImmediately as IShouldServeCache).cache === true + (shouldCacheBeServedImmediately as ShouldServeCache).cache === true ) { debug('Cache should be served immediately.'); if (metadata) { const innerMessage = - shouldCacheBeServedImmediately && (shouldCacheBeServedImmediately as IShouldServeCache).remaining - ? (shouldCacheBeServedImmediately as IShouldServeCache).remaining + shouldCacheBeServedImmediately && (shouldCacheBeServedImmediately as ShouldServeCache).remaining + ? (shouldCacheBeServedImmediately as ShouldServeCache).remaining : ''; debug(`Cache ${displayKey}data: ${innerMessage}`); } @@ -429,9 +434,9 @@ export abstract class IntelligentEngine { await sleep(delayBeforeRefreshMilliseconds); } debug('Directly calling the function or REST API'); - let response: IRestResponse = undefined; + let response: RestResponse = undefined; try { - response = (await this.callApi(apiContext, `GET: ${displayKey}`)) as IRestResponse; + response = (await this.callApi(apiContext, `GET: ${displayKey}`)) as RestResponse; } catch (error) { if (error && error.status && error.status === 304) { const liveHeaders = error.response?.headers || {}; @@ -452,7 +457,7 @@ export abstract class IntelligentEngine { return response; } - private async processResponse(apiContext: ApiContext, response: IRestResponse): Promise { + private async processResponse(apiContext: ApiContext, response: RestResponse): Promise { this.withResponseUpdateMetadata(apiContext, response); const isCacheOk = this.withResponseShouldCacheBeServed(apiContext, response); if (isCacheOk === true) { @@ -474,7 +479,7 @@ export abstract class IntelligentEngine { } } - private async getCachedMetadata(apiContext: ApiContext): Promise { + private async getCachedMetadata(apiContext: ApiContext): Promise { if (apiContext.metadata || apiContext.etag) { debug('Shortcut: apiContext.metadata or apiContext.etag are set'); return; @@ -483,9 +488,9 @@ export abstract class IntelligentEngine { if (!redisKey) { throw new Error('No Redis key provided in apiContext.redisKey.metadata'); } - const cachedMetadata: IRestMetadata = (await apiContext.libraryContext.cacheProvider.getObject( + const cachedMetadata: RestMetadata = (await apiContext.libraryContext.cacheProvider.getObject( redisKey - )) as IRestMetadata; + )) as RestMetadata; // debug('Cached metadata retrieved'); this.recordRedisCost(apiContext, 'get', cachedMetadata); return cachedMetadata; diff --git a/lib/github/crossOrganization.ts b/lib/github/crossOrganization.ts index a1e72dde1..4024d0d17 100644 --- a/lib/github/crossOrganization.ts +++ b/lib/github/crossOrganization.ts @@ -3,7 +3,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { IRestResponse, IIntelligentCacheResponseArray, flattenData } from './core'; +import { RestResponse, IIntelligentCacheResponseArray, flattenData } from './core'; import { CompositeApiContext } from './composite'; import { RestLibrary } from '.'; import { RestCollections } from './collections'; @@ -11,16 +11,16 @@ import { ICacheOptions, IGetOrganizationMembersOptions, IPagedCrossOrganizationCacheOptions, - IPurposefulGetAuthorizationHeader, + PurposefulGetAuthorizationHeader, ITeamMembershipOptions, } from '../../interfaces'; -import { AppPurpose } from '../../business/githubApps'; +import { AppPurpose } from './appPurposes'; -interface IOrganizationsResponse extends IRestResponse { +interface IOrganizationsResponse extends RestResponse { orgs?: any; } -interface ICrossOrganizationDataResponse extends IRestResponse { +interface ICrossOrganizationDataResponse extends RestResponse { // data?: any; } @@ -42,7 +42,7 @@ export class CrossOrganizationCollator { } async orgMembers( - orgsAndTokens: Map, + orgsAndTokens: Map, options: IGetOrganizationMembersOptions, cacheOptions: ICacheOptions ): Promise { @@ -57,13 +57,13 @@ export class CrossOrganizationCollator { return flattenData(data); } - async teams(orgsAndTokens: Map, options, cacheOptions) { + async teams(orgsAndTokens: Map, options, cacheOptions) { const allTeams = await this.getAllTeams(orgsAndTokens, options, cacheOptions); return flattenData(allTeams); } async teamMembers( - orgsAndTokens: Map, + orgsAndTokens: Map, options: ITeamMembershipOptions, cacheOptions: ICacheOptions ): Promise { @@ -90,7 +90,7 @@ export class CrossOrganizationCollator { } async repos( - orgsAndTokens: Map, + orgsAndTokens: Map, options, cacheOptions: ICacheOptions ): Promise { @@ -99,7 +99,7 @@ export class CrossOrganizationCollator { } async repoCollaborators( - orgsAndTokens: Map, + orgsAndTokens: Map, options, cacheOptions: ICacheOptions ): Promise { @@ -126,7 +126,7 @@ export class CrossOrganizationCollator { } async repoTeams( - orgsAndTokens: Map, + orgsAndTokens: Map, options, cacheOptions: ICacheOptions ): Promise { @@ -173,7 +173,7 @@ export class CrossOrganizationCollator { } private async getCrossOrganizationMethod( - orgsAndTokens: Map, + orgsAndTokens: Map, apiName: string, methodName: string, options, @@ -242,7 +242,7 @@ export class CrossOrganizationCollator { private crossOrganizationCollection( capturedThis: CrossOrganizationCollator, - orgsAndTokens: Map, + orgsAndTokens: Map, options, cacheOptions: IPagedCrossOrganizationCacheOptions, innerKeyType, @@ -303,7 +303,7 @@ export class CrossOrganizationCollator { const localOptions = Object.assign(localOptionsTarget, options); delete localOptions.maxAgeSeconds; delete localOptions.backgroundRefresh; - const token = orgsAndTokens.get(orgName.toLowerCase()) as IPurposefulGetAuthorizationHeader; + const token = orgsAndTokens.get(orgName.toLowerCase()) as PurposefulGetAuthorizationHeader; if (!token) { throw new Error(`No token available for the organization ${orgName}`); } @@ -348,7 +348,7 @@ export class CrossOrganizationCollator { } private async getAllTeams( - orgsAndTokens: Map, + orgsAndTokens: Map, options, cacheOptions: IPagedCrossOrganizationCacheOptions ): Promise { @@ -364,7 +364,7 @@ export class CrossOrganizationCollator { } private async getAllRepos( - orgsAndTokens: Map, + orgsAndTokens: Map, options, cacheOptions: IPagedCrossOrganizationCacheOptions ): Promise { diff --git a/lib/github/index.ts b/lib/github/index.ts index 923b0fc81..f9269ca6a 100644 --- a/lib/github/index.ts +++ b/lib/github/index.ts @@ -13,17 +13,26 @@ import { CompositeIntelligentEngine } from './composite'; import { RestCollections } from './collections'; import { CrossOrganizationCollator } from './crossOrganization'; import { LinkMethods } from './links'; -import { IGetAuthorizationHeader, IAuthorizationHeaderValue } from '../../interfaces'; +import { GetAuthorizationHeader, AuthorizationHeaderValue } from '../../interfaces'; import { ICacheHelper } from '../caching'; -import { ICustomAppPurpose } from '../../business/githubApps'; +import { ICustomAppPurpose } from './appPurposes'; +import { CreateError } from '../transitional'; export enum CacheMode { ValidateCache = 'ValidateCache', BackgroundRefresh = 'BackgroundRefresh', } +export enum HttpMethod { + Get = 'GET', + Post = 'POST', + Put = 'PUT', + Patch = 'PATCH', + Delete = 'DELETE', +} + export interface IGitHubPostFunction { - (awaitToken: IGetAuthorizationHeader, api: string, parameters: any): Promise; + (awaitToken: GetAuthorizationHeader, api: string, parameters: any): Promise; } export type OctokitGraphqlOptions = { @@ -128,25 +137,27 @@ export class RestLibrary { hasNextPage?: (any) => boolean; private async resolveAuthorizationHeader( - authorizationHeader: IGetAuthorizationHeader | IAuthorizationHeaderValue | string - ): Promise { + authorizationHeader: GetAuthorizationHeader | AuthorizationHeaderValue | string + ): Promise { let authorizationValue = null; try { - if (typeof authorizationHeader === 'string') { + if (!authorizationHeader) { + throw CreateError.InvalidParameters('No authorization header'); + } else if (typeof authorizationHeader === 'string') { authorizationValue = authorizationHeader as string; } else if (typeof authorizationHeader === 'function') { - let asFunc = authorizationHeader as IGetAuthorizationHeader; - let resolved = asFunc.call(null) as Promise; + let asFunc = authorizationHeader as GetAuthorizationHeader; + let resolved = asFunc.call(null) as Promise; authorizationValue = await resolved; if (typeof resolved === 'function') { - asFunc = resolved as IGetAuthorizationHeader; - resolved = asFunc.call(null) as Promise; + asFunc = resolved as GetAuthorizationHeader; + resolved = asFunc.call(null) as Promise; authorizationValue = await resolved; } } else if (authorizationHeader && authorizationHeader['value']) { - authorizationValue = authorizationHeader as IAuthorizationHeaderValue; + authorizationValue = authorizationHeader as AuthorizationHeaderValue; } else { - throw new Error('Invalid resolveAuthorizationHeader'); + throw CreateError.InvalidParameters('Unknown resolveAuthorizationHeader type'); } } catch (getTokenError) { console.dir(getTokenError); @@ -156,7 +167,7 @@ export class RestLibrary { } async call( - awaitToken: IGetAuthorizationHeader | IAuthorizationHeaderValue | string, + awaitToken: GetAuthorizationHeader | AuthorizationHeaderValue | string, api: string, options, cacheOptions = null @@ -181,20 +192,32 @@ export class RestLibrary { return result; } - request(token, restEndpoint, parameters: any, cacheOptions): Promise { + request(token: GetAuthorizationHeader | string, restEndpoint, parameters: any, cacheOptions): Promise { parameters = parameters || {}; parameters['octokitRequest'] = restEndpoint; return this.call(token, 'request', parameters, cacheOptions); } - requestAsPost(token, restEndpoint, parameters: any): Promise { + requestAsPost(token: GetAuthorizationHeader | string, restEndpoint, parameters: any): Promise { parameters = parameters || {}; parameters['octokitRequest'] = restEndpoint; return this.post(token, 'request', parameters); } + restApi( + token: GetAuthorizationHeader | string, + httpMethod: HttpMethod, + restEndpoint: string, + parameters: any + ): Promise { + const requestUrlValue = `${httpMethod} ${restEndpoint}`; + return httpMethod === HttpMethod.Get + ? this.request(token, requestUrlValue, parameters, {}) + : this.requestAsPost(token, requestUrlValue, parameters); + } + graphql( - token, + token: GetAuthorizationHeader | string, query: string, parameters: any, graphqlOptions: OctokitGraphqlOptions = {} @@ -203,7 +226,7 @@ export class RestLibrary { } graphqlIteration( - token, + token: GetAuthorizationHeader | string, query: string, parameters: any, graphqlOptions: OctokitGraphqlOptions = {} @@ -228,7 +251,7 @@ export class RestLibrary { return this.post(token, api, parameters); } - async post(awaitToken: IGetAuthorizationHeader | string, api: string, options: any): Promise { + async post(awaitToken: GetAuthorizationHeader | string, api: string, options: any): Promise { const method = restApi.IntelligentGitHubEngine.findLibraryMethod(this.github, api); if (!options.headers) { options.headers = {}; @@ -239,14 +262,14 @@ export class RestLibrary { delete options.allowEmptyResponse; massageData = noDataMassage; } - let diagnosticHeaderInformation: IAuthorizationHeaderValue = null; + let diagnosticHeaderInformation: AuthorizationHeaderValue = null; if (!options.headers.authorization) { const value = await this.resolveAuthorizationHeader(awaitToken); - if ((value as IAuthorizationHeaderValue)?.purpose) { - diagnosticHeaderInformation = value as IAuthorizationHeaderValue; + if ((value as AuthorizationHeaderValue)?.purpose) { + diagnosticHeaderInformation = value as AuthorizationHeaderValue; } options.headers.authorization = - typeof value === 'string' ? (value as string) : (value as IAuthorizationHeaderValue).value; + typeof value === 'string' ? (value as string) : (value as AuthorizationHeaderValue).value; } const diagnostic: Record = {}; try { @@ -283,8 +306,15 @@ export class RestLibrary { return finalized; } catch (error) { console.log(`API ${api} POST error: ${error.message}`); - if (error?.message?.includes('Resource not accessible by integration')) { - console.error('Options:'); + if (error?.message?.includes('Unexpected end of JSON input')) { + console.log('Usually a unicorn and bad GitHub 500'); + console.dir(error); + } + if ( + error?.message?.includes('Resource not accessible by integration') || + error?.message?.includes('Not Found') + ) { + console.error('\tOptions:'); { const options = Object.getOwnPropertyNames(diagnostic.options).length > 0 ? diagnostic.options : null; @@ -297,17 +327,17 @@ export class RestLibrary { if (key === 'headers') { const headers = value as Record; const headersKeys = Object.getOwnPropertyNames(headers); - console.log('Headers:'); + console.log('\t\tHeaders:'); for (let j = 0; j < headersKeys.length; j++) { const headerKey = headersKeys[j]; const headerValue = headerKey.toLocaleLowerCase() === 'authorization' ? headers[headerKey].substring(0, 13) + '***' : headers[headerKey]; - console.log(` - ${headerKey}: ${headerValue}`); + console.log(`\t\t - ${headerKey}: ${headerValue}`); } } else { - console.log(`Option: ${key}: ${value}`); + console.log(`\t\tOption: ${key}: ${value}`); } } } @@ -316,38 +346,39 @@ export class RestLibrary { for (let i = 0; i < remainingKeys.length; i++) { const key = remainingKeys[i]; const value = diagnostic[key]; - console.log(`${key}: ${value}`); + console.log(`\t\t${key}: ${value}`); } } } if (diagnosticHeaderInformation) { - console.error('Authorization selection information:'); + console.error('\tAuthorization selection information:'); const { installationId, organizationName, purpose, source } = diagnosticHeaderInformation; - organizationName && console.error(`Header resolved for organization: ${organizationName}`); + organizationName && console.error(`\t\tHeader resolved for organization: ${organizationName}`); const customPurpose = purpose as ICustomAppPurpose; purpose && customPurpose?.isCustomAppPurpose === true && - console.error(`Custom purpose: ${customPurpose.id}`); - purpose && !customPurpose?.isCustomAppPurpose && console.error(`Purpose: ${purpose}`); - installationId && console.error(`Installation ID: ${installationId}`); - source && console.error(`Source: ${source}`); + console.error(`\t\tCustom purpose: ${customPurpose.id}`); + purpose && !customPurpose?.isCustomAppPurpose && console.error(`\t\tPurpose: ${purpose}`); + installationId && console.error(`\t\tInstallation ID: ${installationId}`); + source && console.error(`\t\tSource: ${source}`); } } if (error.status) { - console.log(`Status: ${error.status}`); + console.log(`\tStatus: ${error.status}`); } if (error?.response?.headers && error?.response?.headers['x-github-request-id']) { - console.log(`Request ID: ${error.response.headers['x-github-request-id']}`); + console.log(`\tRequest ID: ${error.response.headers['x-github-request-id']}`); } if (error?.response?.headers && error?.response?.headers['x-ratelimit-remaining']) { - console.log(`Rate limit remaining: ${error.response.headers['x-ratelimit-remaining']}`); + console.log(`\tRate limit remaining: ${error.response.headers['x-ratelimit-remaining']}`); } if (error?.response?.headers && error?.response?.headers['x-ratelimit-used']) { - console.log(`Rate limit used: ${error.response.headers['x-ratelimit-used']}`); + console.log(`\tRate limit used: ${error.response.headers['x-ratelimit-used']}`); } if (shouldErrorShowRequest && error?.request) { console.dir(error.request); } + console.log(); throw error; } } diff --git a/lib/github/restApi.ts b/lib/github/restApi.ts index 7687f2bed..0c1e3f2e7 100644 --- a/lib/github/restApi.ts +++ b/lib/github/restApi.ts @@ -14,19 +14,26 @@ const debugShowStandardBehavior = false; const debugOutputUnregisteredEntityApis = true; import { - IShouldServeCache, + ShouldServeCache, IntelligentEngine, ApiContext, IApiContextCacheValues, IApiContextRedisKeys, ApiContextType, - IRestResponse, - IRestMetadata, + RestResponse, + RestMetadata, } from './core'; import { getEntityDefinitions, GitHubResponseType, ResponseBodyType } from './endpointEntities'; import appPackage from '../../package.json'; -import { IGetAuthorizationHeader, IAuthorizationHeaderValue } from '../../interfaces'; +import { ErrorHelper } from '../transitional'; + +import type { GetAuthorizationHeader, AuthorizationHeaderValue } from '../../interfaces'; +import { + type IGitHubAppConfiguration, + getAppPurposeId, + tryGetAppPurposeAppConfiguration, +} from './appPurposes'; const appVersion = appPackage.version; @@ -70,7 +77,7 @@ export class IntelligentGitHubEngine extends IntelligentEngine { return method; } - async callApi(apiContext: GitHubApiContext, optionalMessage?: string): Promise { + async callApi(apiContext: GitHubApiContext, optionalMessage?: string): Promise { const token = apiContext.token; // CONSIDER: rename apiContext.token *to* something like apiContext.authorization if ( @@ -101,10 +108,11 @@ export class IntelligentGitHubEngine extends IntelligentEngine { } } } + const purpose = apiContext?.tokenSource?.purpose ? getAppPurposeId(apiContext.tokenSource.purpose) : null; if (optionalMessage) { let apiTypeSuffix = apiContext.tokenSource && apiContext.tokenSource.purpose - ? ' [' + apiContext.tokenSource.purpose + ']' + ? ' [' + (purpose || apiContext.tokenSource.purpose) + ']' : ''; if (!apiTypeSuffix && apiContext.tokenSource && apiContext.tokenSource.source) { apiTypeSuffix = ` [token source=${apiContext.tokenSource.source}]`; @@ -139,11 +147,41 @@ export class IntelligentGitHubEngine extends IntelligentEngine { args.push(argOptions); } const thisArgument = apiMethod.thisInstance || null; - const response = await apiMethod.apply(thisArgument, args); - return response; + try { + const response = await apiMethod.apply(thisArgument, args); + return response; + } catch (error) { + const asAny = error as any; + if ( + ErrorHelper.IsNotAuthorized(error) && + asAny?.message === 'Resource not accessible by integration' && + apiContext.tokenSource + ) { + let appConfig: IGitHubAppConfiguration = null; + if (apiContext?.tokenSource?.purpose && apiContext?.tokenSource?.organizationName) { + appConfig = tryGetAppPurposeAppConfiguration( + apiContext.tokenSource.purpose, + apiContext.tokenSource.organizationName + ); + } + asAny.source = apiContext.tokenSource.source; + const additional: string[] = []; + purpose && additional.push(`purpose=${purpose}`); + appConfig?.appId && additional.push(`appId=${appConfig.appId}`); + appConfig?.slug && additional.push(`slug=${appConfig.slug}`); + apiContext?.tokenSource?.installationId && + additional.push(`installationId=${apiContext.tokenSource.installationId}`); + apiContext?.tokenSource?.organizationName && + additional.push(`organization=${apiContext.tokenSource.organizationName}`); + const extra = ' ' + additional.join(', '); + debug(`Additional installation context added to message for 403: ${extra}`); + asAny.message += extra; + } + throw error; + } } - processMetadataBeforeCall(apiContext: ApiContext, metadata: IRestMetadata) { + processMetadataBeforeCall(apiContext: ApiContext, metadata: RestMetadata) { if ( metadata && metadata.av && @@ -165,11 +203,11 @@ export class IntelligentGitHubEngine extends IntelligentEngine { return metadata; } - withResponseUpdateMetadata(apiContext: ApiContext, response: IRestResponse) { + withResponseUpdateMetadata(apiContext: ApiContext, response: RestResponse) { return response; } - optionalStripResponse(apiContext: ApiContext, response: IRestResponse): IRestResponse { + optionalStripResponse(apiContext: ApiContext, response: RestResponse): RestResponse { const clonedResponse = Object.assign({}, response); if (response.headers) { const clonedHeaders = StripGitHubEntity( @@ -257,7 +295,7 @@ export class IntelligentGitHubEngine extends IntelligentEngine { return clonedResponse; } - reduceMetadataToCacheFromResponse(apiContext: ApiContext, response: IRestResponse): any { + reduceMetadataToCacheFromResponse(apiContext: ApiContext, response: RestResponse): any { const headers = response ? response.headers : null; if (headers?.etag) { const reduced: IReducedGitHubMetadata = { @@ -282,8 +320,8 @@ export class IntelligentGitHubEngine extends IntelligentEngine { withResponseShouldCacheBeServed( apiContext: ApiContext, - response: IRestResponse - ): boolean | IShouldServeCache { + response: RestResponse + ): boolean | ShouldServeCache { if (response === undefined) { throw new Error('The response was undefined and unable to process.'); } @@ -320,8 +358,8 @@ export class IntelligentGitHubEngine extends IntelligentEngine { return cacheOk; } - getResponseMetadata(apiContext: ApiContext, response: IRestResponse): IRestMetadata { - const md: IRestMetadata = { + getResponseMetadata(apiContext: ApiContext, response: RestResponse): RestMetadata { + const md: RestMetadata = { headers: response.headers, status: response.status, }; @@ -330,12 +368,12 @@ export class IntelligentGitHubEngine extends IntelligentEngine { withMetadataShouldCacheBeServed( apiContext: ApiContext, - metadata: IRestMetadata - ): boolean | IShouldServeCache { + metadata: RestMetadata + ): boolean | ShouldServeCache { // result can be falsy OR an object; { cache: true, refresh: true } // cache: whether to use the cache, if available // refresh: whether to refresh in the background for a newer value - let shouldServeCache: IShouldServeCache | boolean = false; + let shouldServeCache: ShouldServeCache | boolean = false; const maxAgeSeconds = apiContext.maxAgeSeconds; const updatedIso = metadata ? metadata.updated : null; const refreshingIso = metadata ? metadata.refreshing : null; @@ -408,7 +446,7 @@ export class GitHubApiContext extends ApiContext { private _apiMethod: any; private _redisKeys: IApiContextRedisKeys; private _cacheValues: IApiContextCacheValues; - private _token: string | IGetAuthorizationHeader | IAuthorizationHeaderValue; + private _token: string | GetAuthorizationHeader | AuthorizationHeaderValue; public fakeLink?: IGitHubLink; @@ -434,7 +472,7 @@ export class GitHubApiContext extends ApiContext { }; } - get token(): string | IGetAuthorizationHeader | IAuthorizationHeaderValue { + get token(): string | GetAuthorizationHeader | AuthorizationHeaderValue { return this._token; } @@ -473,9 +511,9 @@ export class GitHubApiContext extends ApiContext { this.libraryContext = libraryContext; } - overrideToken(token: string | IGetAuthorizationHeader | IAuthorizationHeaderValue) { + overrideToken(token: string | GetAuthorizationHeader | AuthorizationHeaderValue) { if (token && token['value']) { - const asPair = token as IAuthorizationHeaderValue; + const asPair = token as AuthorizationHeaderValue; this._token = asPair.value; this.tokenSource = asPair; } else if (typeof token === 'string') { diff --git a/business/githubApps/tokenManager.ts b/lib/github/tokenManager.ts similarity index 65% rename from business/githubApps/tokenManager.ts rename to lib/github/tokenManager.ts index 560ba6e7f..413b4940e 100644 --- a/business/githubApps/tokenManager.ts +++ b/lib/github/tokenManager.ts @@ -15,20 +15,21 @@ import { GitHubAppPurposes, AppPurposeTypes, getAppPurposeId, -} from '.'; +} from './appPurposes'; import { GitHubAppTokens } from './appTokens'; -import { IAuthorizationHeaderValue, NoCacheNoBackground } from '../../interfaces'; -import { OrganizationSetting } from '../../entities/organizationSettings/organizationSetting'; -import { readFileToText } from '../../utils'; -import { Operations, OperationsCore, Organization } from '..'; -import { CreateError } from '../../transitional'; +import { AuthorizationHeaderValue, GetAuthorizationHeader, NoCacheNoBackground } from '../../interfaces'; +import { OrganizationSetting } from '../../business/entities/organizationSettings/organizationSetting'; +import { readFileToText } from '../utils'; +import { Operations, OperationsCore, Organization } from '../../business'; +import { CreateError } from '../transitional'; +import { shuffle } from 'lodash'; -export interface IGitHubRateLimit { +export type GitHubRateLimit = { limit: number; remaining: number; reset: number; used: number; -} +}; // Installation redirect format: // /setup/app/APP_ID?installation_id=INSTALLATION_ID&setup_action=install @@ -53,7 +54,7 @@ export class GitHubTokenManager { private _forceInstanceTokensToPurpose: AppPurposeTypes; private _allowReadOnlyFallbackToOtherInstallations: boolean; - static RegisterManagerForOperations(operations: OperationsCore, manager: GitHubTokenManager) { + private static RegisterManagerForOperations(operations: OperationsCore, manager: GitHubTokenManager) { GitHubTokenManager._managersForOperations.set(operations, manager); } @@ -65,9 +66,15 @@ export class GitHubTokenManager { if (!options) { throw new Error('options required'); } + const executionEnvironment = options.executionEnvironment; this.#options = options; GitHubTokenManager._forceBackgroundTokens = - options.app.isBackgroundJob && !options.app.enableAllGitHubApps; + executionEnvironment.isJob && !executionEnvironment.enableAllGitHubApps; + GitHubTokenManager.RegisterManagerForOperations(options.operations, this); + } + + private operations() { + return this.#options.operations as Operations; } private getFallbackList(input: AppPurposeTypes[]) { @@ -146,6 +153,8 @@ export class GitHubTokenManager { } private getPurposeDisplayId(purpose: AppPurposeTypes) { + // is this identical to the method getAppPurposeId? + const asCustom = purpose as ICustomAppPurpose; if (asCustom?.isCustomAppPurpose === true) { return asCustom.id; @@ -161,14 +170,38 @@ export class GitHubTokenManager { return null; } + async ensureConfigurationAppInitialized( + customPurpose: AppPurposeTypes, + customPurposeConfiguration: IGitHubAppConfiguration + ): Promise { + const appId = customPurposeConfiguration.appId; + const asCustomPurpose = this.getCustomPurpose(customPurpose); + if (!asCustomPurpose?.isCustomAppPurpose) { + throw CreateError.InvalidParameters(`The purpose ${customPurpose} is not a custom app purpose`); + } + let app = this._appsById.get(appId); + if (!app) { + debug(`initializing app for custom purpose ${asCustomPurpose.id} with custom configuration`); + app = await this.initializeApp(asCustomPurpose, customPurposeConfiguration); + } + if (!app) { + throw CreateError.InvalidParameters( + `Error initializing purpose ${this.getPurposeDisplayId(customPurpose)}` + ); + } + return app; + } + async getOrganizationAuthorizationHeader( organizationName: string, preferredPurpose: AppPurposeTypes, organizationSettings: OrganizationSetting, appAuthenticationType: GitHubAppAuthenticationType - ): Promise { + ): Promise { debug( - `getOrganizationAuthorizationHeader(${organizationName}, ${preferredPurpose}, ${appAuthenticationType})` + `getOrganizationAuthorizationHeader(${organizationName}, ${this.getPurposeDisplayId( + preferredPurpose + )}, ${appAuthenticationType})` ); const installationIdPair = this.getPrioritizedOrganizationInstallationId( preferredPurpose, @@ -177,8 +210,10 @@ export class GitHubTokenManager { appAuthenticationType ); if (!installationIdPair) { - throw new Error( - `GitHubTokenManager: organization ${organizationName} does not have a configured GitHub App installation, or, the installation information is not in this environment. The API preferred purpose was ${preferredPurpose} with the selection type ${appAuthenticationType}.` + throw CreateError.InvalidParameters( + `GitHubTokenManager: organization ${organizationName} does not have a configured GitHub App installation, or, the installation information is not in this environment. The API preferred purpose was ${getAppPurposeId( + preferredPurpose + )} with the selection type ${appAuthenticationType}.` ); } if ( @@ -232,16 +267,18 @@ export class GitHubTokenManager { ); const value = await app.getInstallationToken(installationIdPair.installationId, organizationName); debug( - `returned installation ID pair: installationId=${value?.installationId}, source=${value?.source}, purpose=${value?.purpose}` + `returned installation ID pair: installationId=${value?.installationId}, source=${value?.source}, purpose=${this.getPurposeDisplayId( + value?.purpose + )}` ); return value; } - async getInstallationAuthorizationHeader( + getInstallationAuthorizationHeader( appId: number, installationId: number, organizationName: string - ): Promise { + ): Promise { const app = this._appsById.get(appId); if (!app) { throw new Error(`App ID=${appId} is not configured in this application instance`); @@ -249,10 +286,52 @@ export class GitHubTokenManager { return app.getInstallationToken(installationId, organizationName); } - getAppForPurpose(purpose: AppPurposeTypes) { + getAppForPurpose(purpose: AppPurposeTypes, organizationName?: string) { + const asCustomPurpose = this.getCustomPurpose(purpose); + if (asCustomPurpose?.getForOrganizationName) { + const configForOrganization = asCustomPurpose.getForOrganizationName(organizationName); + const appId = configForOrganization.appId; + if (appId) { + return this._appsById.get(appId); + } + } else if (asCustomPurpose?.getApplicationConfigurationForInitialization) { + const config = asCustomPurpose.getApplicationConfigurationForInitialization(); + const appId = config.appId; + if (appId) { + return this._appsById.get(appId); + } + } return this._apps.get(purpose); } + getAnyConfiguredInstallationIdForAppId(operations: Operations, appId: number) { + const orgs = operations.getOrganizations(); + for (const org of orgs) { + const settings = org.getDynamicSettings(); + if (settings?.installations) { + for (const { appId: appConfiguredId, installationId } of settings.installations) { + if (appConfiguredId === appId) { + return { installationId, organizationName: org.name }; + } + } + } + } + } + + getAnyConfiguredInstallationIdForAnyApp(operations: Operations) { + const orgs = shuffle(operations.getOrganizations()); + for (const org of orgs) { + const settings = org.getDynamicSettings(); + if (settings?.installations) { + const installs = shuffle(settings.installations); + const configuredInstalls = installs.filter((i) => this._appsById.has(i.appId)); + for (const { installationId, appId } of configuredInstalls) { + return { installationId, organizationName: org.name, appId }; + } + } + } + } + getInstallationIdForOrganization(purpose: AppPurposeTypes, organization: Organization) { const settings = organization.getDynamicSettings(); if (settings?.installations) { @@ -263,7 +342,18 @@ export class GitHubTokenManager { } } } - // CONSIDER: custom purposes could expose the installation ID here + const asCustomPurpose = this.getCustomPurpose(purpose); + if (asCustomPurpose?.getForOrganizationName) { + const configForOrganization = asCustomPurpose.getForOrganizationName(organization.name); + if (configForOrganization.slug) { + throw CreateError.NotImplemented( + `While a custom purpose is configured for the "${organization.name}" with the app ${configForOrganization.slug}, the installation ID is not yet available with this call. This is a known limitation.` + ); + } + throw CreateError.NotImplemented( + `This custom purpose is not configured for the "${organization.name}" org with the app ${configForOrganization.slug}, the installation ID is not yet available with this call. This is a known limitation.` + ); + } if (!organization.hasDynamicSettings) { throw CreateError.InvalidParameters( `Organization ${organization.name} does not have dynamic settings or purpose-directed configuration` @@ -271,6 +361,73 @@ export class GitHubTokenManager { } } + getAuthorizationHeaderForAnyApp(): Promise { + const anyConfigured = this.getAnyConfiguredInstallationIdForAnyApp(this.operations()); + if (anyConfigured) { + return this.getInstallationAuthorizationHeader( + anyConfigured.appId, + anyConfigured.installationId, + anyConfigured.organizationName + ); + } + throw CreateError.InvalidParameters('No configured applications available.'); + } + + async getAppInformation(purpose: AppPurposeTypes, organizationName?: string) { + const appTokens = this.getAppForPurpose(purpose, organizationName); + if (!appTokens) { + throw CreateError.InvalidParameters(`No app configured yet for purpose ${purpose}`); + } + const slug = appTokens.slug; + if (!slug) { + throw CreateError.InvalidParameters(`No slug configured for purpose ${purpose}`); + } + return this.getAppInformationBySlug(this.operations(), slug); + } + + async getAppInformationBySlug(operations: Operations, slug: string) { + let appId: number = null; + for (const entry of this._appSlugs.entries()) { + if (entry[1] === slug) { + appId = entry[0]; + break; + } + } + let authorizationHeader: GetAuthorizationHeader = null; + // Have the app call itself via the slug-based API (works if it's a private single-org app) + if (appId) { + const anyConfiguredForApp = this.getAnyConfiguredInstallationIdForAppId(operations, appId); + if (anyConfiguredForApp) { + authorizationHeader = this.getInstallationAuthorizationHeader.bind( + this, + appId, + anyConfiguredForApp.installationId, + anyConfiguredForApp.organizationName + ); + } + } + // Call using any configured app + if (!authorizationHeader) { + const anyConfigured = this.getAnyConfiguredInstallationIdForAnyApp(operations); + if (anyConfigured) { + authorizationHeader = this.getInstallationAuthorizationHeader.bind( + this, + anyConfigured.appId, + anyConfigured.installationId, + anyConfigured.organizationName + ); + } + } + // Fallback to a static token + if (!authorizationHeader) { + authorizationHeader = operations.getPublicReadOnlyStaticToken.bind(operations); + } + const value = await operations.github.post(authorizationHeader, 'apps.getBySlug', { + app_slug: slug, + }); + return value; + } + async getRateLimitInformation(purpose: AppPurposeTypes, organization: Organization) { const settings = organization.getDynamicSettings(); if (settings?.installations) { @@ -289,7 +446,7 @@ export class GitHubTokenManager { NoCacheNoBackground ); if (value?.rate) { - return value.rate as IGitHubRateLimit; + return value.rate as GitHubRateLimit; } console.warn(value); throw CreateError.InvalidParameters('No rate limit information returned'); @@ -325,12 +482,18 @@ export class GitHubTokenManager { const allPurposes = GitHubAppPurposes.AllAvailableAppPurposes; const fallbackPurposePriorities = this.getFallbackList(allPurposes); const customPurposes = allPurposes.filter((p) => (p as ICustomAppPurpose).isCustomAppPurpose === true); + const matchingCustomPurposes = + (preferredPurpose as ICustomAppPurpose)?.isCustomAppPurpose === true + ? customPurposes.filter( + (cp) => (cp as ICustomAppPurpose).id === (preferredPurpose as ICustomAppPurpose).id + ) + : customPurposes; let order = this.isForcingBackgroundJobType() === true ? fallbackPurposePriorities : [preferredPurpose, ...fallbackPurposePriorities]; if (appAuthenticationType === GitHubAppAuthenticationType.ForceSpecificInstallation) { - order = [preferredPurpose, ...customPurposes]; + order = [preferredPurpose, ...matchingCustomPurposes]; } for (const purpose of order) { let customAppPurpose = purpose as ICustomAppPurpose; @@ -383,11 +546,12 @@ export class GitHubTokenManager { // Not base64-encoded, use the CreateFromString method. skipDecodingBase64 = true; } + const slug = appConfig.slug; const friendlyName = customPurpose?.name || appConfig.description || 'Unknown'; const baseUrl = appConfig.baseUrl; const app = skipDecodingBase64 - ? GitHubAppTokens.CreateFromString(purpose, friendlyName, appId, key, baseUrl) - : GitHubAppTokens.CreateFromBase64EncodedFileString(purpose, friendlyName, appId, key, baseUrl); + ? GitHubAppTokens.CreateFromString(purpose, slug, friendlyName, appId, key, baseUrl) + : GitHubAppTokens.CreateFromBase64EncodedFileString(purpose, slug, friendlyName, appId, key, baseUrl); const hasCustomConfigurationByOrganization = customPurpose?.getForOrganizationName; const standardPurpose = purpose as AppPurpose; if (!hasCustomConfigurationByOrganization) { diff --git a/lib/graphProvider/microsoftGraphProvider.ts b/lib/graphProvider/microsoftGraphProvider.ts index 8cdb4f5e0..0d5922e91 100644 --- a/lib/graphProvider/microsoftGraphProvider.ts +++ b/lib/graphProvider/microsoftGraphProvider.ts @@ -8,6 +8,7 @@ import cache from 'memory-cache'; import axios, { AxiosError } from 'axios'; import querystring from 'querystring'; +import validator from 'validator'; import { IGraphProvider, @@ -17,7 +18,7 @@ import { IGraphGroup, GraphUserType, } from '.'; -import { ErrorHelper, CreateError, splitSemiColonCommas } from '../../transitional'; +import { ErrorHelper, CreateError, splitSemiColonCommas } from '../transitional'; import { ICacheHelper } from '../caching'; const axios12BufferDecompressionBugHeaderAddition = true; @@ -32,20 +33,68 @@ export interface IMicrosoftGraphProviderOptions { skipManagerLookupForIds?: string; } +export type MicrosoftGraphGroupMembersOptions = { + getCount?: boolean; + maximumPages?: number; + throwOnMaximumPages?: boolean; + skipCache?: boolean; + additionalSelectValues?: string[]; + membership?: MicrosoftGraphGroupMembershipType; +}; + +export enum MicrosoftGraphGroupMembershipType { + Transitive = 'transitiveMembers', + Direct = 'members', +} + +export type MicrosoftGraphGroupMember = IGraphGroupMember & { + userType?: GraphUserType; +}; + +export function microsoftGraphUserTypeFromString(type: string): GraphUserType { + if (!type) { + return; + } + switch (type) { + case GraphUserType.Guest: + return GraphUserType.Guest; + case GraphUserType.Member: + return GraphUserType.Member; + default: + return GraphUserType.Unknown; + } +} + +type GraphCheckMembersRequest = { + ids: string[]; +}; + +type GraphCheckMembersResponse = { + value: string[]; +}; + const graphBaseUrl = 'https://graph.microsoft.com/v1.0/'; const odataNextLink = '@odata.nextLink'; -const defaultCachePeriodMinutes = 60; +const defaultCachePeriodMinutes = 60 * 36; // 36 hours const attemptCacheGet = true; -interface IGraphOptions { +type MicrosoftGraphCallOptions = { selectValues?: string; filterValues?: string; orderBy?: string; body?: any; count?: boolean; consistencyLevel?: 'eventual'; -} +}; + +type GraphCacheOptions = { + skipCache?: boolean; + maximumPages?: number; + throwOnMaximumPages?: boolean; +}; + +type GraphOptions = MicrosoftGraphCallOptions & GraphCacheOptions; export class MicrosoftGraphProvider implements IGraphProvider { #_tokenCacheMilliseconds: number; @@ -80,9 +129,26 @@ export class MicrosoftGraphProvider implements IGraphProvider { } async isUserInGroup(corporateId: string, securityGroupId: string): Promise { - // TODO: refactor for efficient use of Microsoft Graph's checkMemberObjects https://docs.microsoft.com/en-us/graph/api/group-checkmemberobjects?view=graph-rest-1.0&tabs=http - const members = await this.getGroupMembers(securityGroupId); - return members.filter((m) => m.id === corporateId).length > 0; + // Formerly used a very inefficient approach: + // const members = await this.getGroupMembers(securityGroupId); + // return members.filter((m) => m.id === corporateId).length > 0; + return await this.checkMemberObjectsForUserId(corporateId, securityGroupId); + } + + private async checkMemberObjectsForUserId(corporateId: string, securityGroupId: string): Promise { + const requestBody: GraphCheckMembersRequest = { + ids: [securityGroupId], + }; + const url = `${graphBaseUrl}users/${corporateId}/checkMemberObjects`; + const response = await this.request( + url, + requestBody, + null, + true + ); /* no cache */ + const foundGroupIds = response.value; + const found = foundGroupIds.includes(securityGroupId); + return found; } private async getTokenThenEntity(aadId: string, resource: string): Promise { @@ -162,12 +228,18 @@ export class MicrosoftGraphProvider implements IGraphProvider { } async getGroup(corporateGroupId: string): Promise { + const selectValues = 'description,displayName,id,mail,mailNickname'; + // if (additionalSelectValues) { + // selectValues = Array.from( + // new Set([...selectValues.split(','), ...additionalSelectValues]).values() + // ).join(','); + // } // prettier-ignore const response = await this.lookupInGraph([ 'groups', corporateGroupId, ], { - selectValues: 'description,displayName,id,mail,mailNickname', + selectValues, }); return response; } @@ -234,8 +306,8 @@ export class MicrosoftGraphProvider implements IGraphProvider { ], { filterValues: `mail eq '${mail}'`, // encodeURIComponent( selectValues: 'id', - count: true, - consistencyLevel: 'eventual', + // count: true, + // consistencyLevel: 'eventual', })) as any[]; if (!response || response.length === 0) { return null; @@ -357,26 +429,50 @@ export class MicrosoftGraphProvider implements IGraphProvider { }); } - async getGroupMembers(corporateGroupId: string): Promise { + async getGroupMembers( + corporateGroupId: string, + options?: MicrosoftGraphGroupMembersOptions + ): Promise { + const defaultSelectSet = ['id', 'userPrincipalName']; + const selectValuesSet = new Set([ + ...defaultSelectSet, + ...(options?.additionalSelectValues || []), + ]); + const graphOptions: GraphOptions = { + selectValues: Array.from(selectValuesSet.values()).join(','), + }; + if (options?.getCount !== undefined) { + graphOptions.count = true; + graphOptions.consistencyLevel = 'eventual'; + } + if (options?.maximumPages !== undefined) { + graphOptions.maximumPages = options.maximumPages; + } + if (options?.throwOnMaximumPages !== undefined) { + graphOptions.throwOnMaximumPages = options.throwOnMaximumPages; + } + const lookupType = options?.membership || MicrosoftGraphGroupMembershipType.Transitive; + const includesUserType = selectValuesSet.has('userType'); const response = (await this.lookupInGraph( - [ - 'groups', - corporateGroupId, - 'transitiveMembers', // transitiveMembers or members - ], - { - selectValues: 'id,userPrincipalName', - } + ['groups', corporateGroupId, lookupType], + graphOptions )) as any[]; - // may be a caching bug: if (Array.isArray(response)) { return response.map((entry) => { - return { id: entry.id, userPrincipalName: entry.userPrincipalName }; + return { + id: entry.id, + userPrincipalName: entry.userPrincipalName, + userType: includesUserType ? microsoftGraphUserTypeFromString(entry.userType) : undefined, + }; }); } const subResponse = (response as any).value ? (response as any).value : []; return subResponse.map((entry) => { - return { id: entry.id, userPrincipalName: entry.userPrincipalName }; + return { + id: entry.id, + userPrincipalName: entry.userPrincipalName, + userType: includesUserType ? microsoftGraphUserTypeFromString(entry.userType) : undefined, + }; }); } @@ -384,12 +480,18 @@ export class MicrosoftGraphProvider implements IGraphProvider { if (!minimum3Characters || minimum3Characters.length < 3) { throw new Error(`Minimum 3 characters required: ${minimum3Characters}`); } + + let filterValues = `securityEnabled eq true and (startswith(displayName, '${minimum3Characters}') or startswith(mailNickname, '${minimum3Characters}'))`; + if (validator.isUUID(minimum3Characters)) { + filterValues = `securityEnabled eq true and (id eq '${minimum3Characters}' or startswith(displayName, '${minimum3Characters}') or startswith(mailNickname, '${minimum3Characters}'))`; + } + // NOTE: this is currently explicitly looking for Security Groups only // prettier-ignore let response = (await this.lookupInGraph([ 'groups', ], { - filterValues: `securityEnabled eq true and (startswith(displayName, '${minimum3Characters}') or startswith(mailNickname, '${minimum3Characters}'))`, + filterValues, selectValues: 'id,displayName,mailNickname', })) as any[]; if (!response.filter && (response as any).value?.filter) { @@ -488,8 +590,9 @@ export class MicrosoftGraphProvider implements IGraphProvider { } } - private async lookupInGraph(entityPath: string[], options: IGraphOptions): Promise { + private async lookupInGraph(entityPath: string[], options: GraphOptions): Promise { // initial hacking on top of the API + const skipCache = options?.skipCache === true; const subUrl = entityPath.map((item) => encodeURIComponent(item)).join('/'); const queries = {}; if (options.filterValues) { @@ -501,12 +604,15 @@ export class MicrosoftGraphProvider implements IGraphProvider { if (options.orderBy) { queries['$orderby'] = options.orderBy; } + if (options.count === true) { + queries['$count'] = 'true'; + } let hasArray = false; let value = null; let url = `${graphBaseUrl}${subUrl}?${querystring.stringify(queries)}`; const originalUrl = url; try { - if (this.#_cache && attemptCacheGet) { + if (this.#_cache && attemptCacheGet && !skipCache) { value = await this.#_cache.getObject(url); if (value?.cache) { if (Array.isArray(value.cache) && value.cache.length === 0) { @@ -522,9 +628,10 @@ export class MicrosoftGraphProvider implements IGraphProvider { console.warn(error); } let pages = 0; + const maximumPages = options?.maximumPages; do { const consistencyLevel = options.consistencyLevel; - const body = await this.request(url, options.body, consistencyLevel); + const body = await this.request(url, options.body, consistencyLevel, skipCache); if (body.value && pages === 0) { hasArray = body && body.value && Array.isArray(body.value); if (hasArray) { @@ -539,14 +646,25 @@ export class MicrosoftGraphProvider implements IGraphProvider { } else { throw new Error(`Page ${pages} in response is not an array type but had a link: ${url}`); } + if (body && body['@odata.count'] !== undefined) { + const count = body['@odata.count']; + // NOTE: we don't store or cache or return this today + console.log(`Total objects in response: ${count}`); + } ++pages; url = body && body[odataNextLink] ? body[odataNextLink] : null; - } while (url); + } while (url && (maximumPages ? pages < maximumPages : true)); + if (pages >= maximumPages) { + if (options.throwOnMaximumPages) { + throw CreateError.InvalidParameters('Maximum pages exceeded for this resource'); + } + console.warn(`WARN: Maximum pages exceeded for this resource: ${originalUrl}`); + } if (this.#_cache) { try { this.#_cache .setObjectWithExpire(originalUrl, { cache: value }, defaultCachePeriodMinutes) - .then((ok) => {}) + .then(() => {}) .catch((err) => { console.warn(err); }); @@ -557,10 +675,15 @@ export class MicrosoftGraphProvider implements IGraphProvider { return value; } - private async request(url: string, body?: any, eventualConsistency?: string): Promise { + private async request( + url: string, + body?: any, + eventualConsistency?: string, + skipCache?: boolean + ): Promise { const token = await this.getToken(); const method = body ? 'post' : 'get'; - if (this.#_cache && attemptCacheGet && method === 'get') { + if (this.#_cache && attemptCacheGet && method === 'get' && !skipCache) { try { const value = await this.#_cache.getObject(url); if (value?.cache) { @@ -584,7 +707,7 @@ export class MicrosoftGraphProvider implements IGraphProvider { } if (eventualConsistency) { - // headers.ConsistencyLevel = eventualConsistency; + headers['ConsistencyLevel'] = eventualConsistency; } const response = await axios({ url, @@ -596,13 +719,12 @@ export class MicrosoftGraphProvider implements IGraphProvider { throw CreateError.ServerError('Empty response'); } if ((response.data as any).error?.message) { - // axios returns unknown now - throw CreateError.InvalidParameters((response.data as any).error.message); // axios returns unknown now + throw CreateError.InvalidParameters((response.data as any).error.message); } if (this.#_cache && method === 'get') { this.#_cache .setObjectWithExpire(url, { cache: response.data }, defaultCachePeriodMinutes) - .then((ok) => {}) + .then(() => {}) .catch((err) => {}); } return response.data; diff --git a/lib/linkProviders/table/tableLinkProvider.ts b/lib/linkProviders/table/tableLinkProvider.ts index 6e4d1c6dc..282a034c4 100644 --- a/lib/linkProviders/table/tableLinkProvider.ts +++ b/lib/linkProviders/table/tableLinkProvider.ts @@ -28,7 +28,7 @@ import { CorporateTableLink } from './tableLink'; import { ILinkProvider } from '..'; import tableEntity from '../../tableEntity'; -import { ErrorHelper } from '../../../transitional'; +import { ErrorHelper } from '../../transitional'; import { decryptEntityAsync, encryptEntityAsync, IEncryptionOptions } from '../../encryption'; import { IKeyVaultSecretResolver } from '../../keyVaultResolver'; diff --git a/lib/mailProvider/index.ts b/lib/mailProvider/index.ts index a334f0340..6ddac4228 100644 --- a/lib/mailProvider/index.ts +++ b/lib/mailProvider/index.ts @@ -3,6 +3,9 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +import { promises as fs } from 'fs'; +import { randomUUID } from 'crypto'; + import MockMailService from './mockMailService'; import SmtpMailService from './smtpMailService'; import AzureServiceBus from './azureServiceBus'; @@ -20,6 +23,46 @@ export interface IMail { correlationId?: string; senderProfile?: string; replyTo?: string; + attachments?: MailAttachment[]; + linkedResources?: MailAttachment[]; +} + +export type MailAttachment = { + name: string; + contentId: string; + contentType: string; + base64Value: string; +}; + +export async function createMailAttachment( + localPath: string, + name: string, + contentType: string, + contentId?: string +): Promise { + const realContentId = contentId || randomUUID(); + const content = await fs.readFile(localPath, 'base64'); + return { + name, + contentId: realContentId, + contentType, + base64Value: content, + }; +} + +export function createMailAttachmentFromBase64( + base64contents: string, + name: string, + contentType: string, + contentId?: string +): MailAttachment { + const realContentId = contentId || randomUUID(); + return { + name, + contentId: realContentId, + contentType, + base64Value: base64contents, + }; } export interface IMailProvider { diff --git a/lib/pugViewServices.ts b/lib/pugViewServices.ts index 2fcd7a98b..cf91bb7c9 100644 --- a/lib/pugViewServices.ts +++ b/lib/pugViewServices.ts @@ -5,7 +5,7 @@ import _ from 'lodash'; import moment from 'moment'; -import octicons from 'octicons'; //const octicons = require('octicons'); +import octicons from '@primer/octicons'; import fileSize from 'file-size'; import languageMap from 'language-map'; import validator from 'validator'; diff --git a/transitional.ts b/lib/transitional.ts similarity index 75% rename from transitional.ts rename to lib/transitional.ts index b2bc2813b..e0806ba54 100644 --- a/transitional.ts +++ b/lib/transitional.ts @@ -7,19 +7,22 @@ import crypto from 'crypto'; import githubUsernameRegex from 'github-username-regex'; import { AxiosError } from 'axios'; -import appPackage from './package.json'; -import type { ICreateRepositoryApiResult } from './api/createRepo'; -import { Repository } from './business/repository'; +import appPackage from '../package.json'; +import type { ICreateRepositoryApiResult } from '../api/createRepo'; +import { Repository } from '../business/repository'; import { GitHubRepositoryPermission, - IDictionary, - IFunctionPromise, - IProviders, - ISettledValue, - ReposAppRequest, + type ICorporateLink, + type IDictionary, + type IFunctionPromise, + type IGitHubCollaboratorPermissions, + type IProviders, + type ISettledValue, + type ReposAppRequest, SettledState, -} from './interfaces'; -import { Organization } from './business'; +} from '../interfaces'; +import { ITeamRepositoryPermission, Organization } from '../business'; +import { ILinkProvider } from './linkProviders'; const packageVariableName = 'static-react-package-name'; export function hasStaticReactClientApp() { @@ -53,19 +56,34 @@ export function SettleToStateValue(promise: Promise): Promise { + return new Promise((resolve, reject) => { + const chunks: Buffer[] = []; + readableStream.on('data', (data: Buffer | string) => { + chunks.push(data instanceof Buffer ? data : Buffer.from(data)); + }); + readableStream.on('end', () => { + resolve(Buffer.concat(chunks)); + }); + readableStream.on('error', reject); + }); } export function isPermissionBetterThan( @@ -75,8 +93,8 @@ export function isPermissionBetterThan( if (!currentBest) { return true; } - const comparison = MassagePermissionsToGitHubRepositoryPermission(currentBest); - switch (MassagePermissionsToGitHubRepositoryPermission(newConsideration)) { + const comparison = projectCollaboratorPermissionToGitHubRepositoryPermission(currentBest); + switch (projectCollaboratorPermissionToGitHubRepositoryPermission(newConsideration)) { case GitHubRepositoryPermission.Admin: return true; case GitHubRepositoryPermission.Maintain: @@ -103,7 +121,9 @@ export function isPermissionBetterThan( return false; } -export function MassagePermissionsToGitHubRepositoryPermission(value: string): GitHubRepositoryPermission { +export function projectCollaboratorPermissionToGitHubRepositoryPermission( + value: string +): GitHubRepositoryPermission { // collaborator level APIs return a more generic read/write value, lead to some bad caches in the past... // TODO: support new collaboration values as they come online for Enterprise Cloud! switch (value) { @@ -120,15 +140,15 @@ export function MassagePermissionsToGitHubRepositoryPermission(value: string): G case 'read': return GitHubRepositoryPermission.Pull; default: - throw new Error( - `Invalid ${value} GitHub repository permission [massagePermissionsToGitHubRepositoryPermission]` + throw CreateError.InvalidParameters( + `Invalid ${value} GitHub repository permission [projectCollaboratorPermissionsToGitHubRepositoryPermission]` ); } } export class CreateError { - static CreateStatusCodeError(code: number, message?: string): Error { - const error = new Error(message); + static CreateStatusCodeError(code: number, message?: string, cause?: Error): Error { + const error = cause ? new Error(message, { cause }) : new Error(message); error['status'] = code; return error; } @@ -150,16 +170,20 @@ export class CreateError { return ErrorHelper.SetInnerError(CreateError.CreateStatusCodeError(400, message), innerError); } - static NotAuthenticated(message: string): Error { - return CreateError.CreateStatusCodeError(401, message); + static NotAuthenticated(message: string, cause?: Error): Error { + return CreateError.CreateStatusCodeError(401, message, cause); } - static NotAuthorized(message: string): Error { - return CreateError.CreateStatusCodeError(403, message); + static NotImplemented(message?: string, cause?: Error): Error { + return CreateError.CreateStatusCodeError(500, message || 'This scenario is not yet implemented', cause); } - static ServerError(message: string, innerError?: Error): Error { - return ErrorHelper.SetInnerError(CreateError.CreateStatusCodeError(500, message), innerError); + static NotAuthorized(message: string, cause?: Error): Error { + return CreateError.CreateStatusCodeError(403, message, cause); + } + + static ServerError(message: string, cause?: Error): Error { + return CreateError.CreateStatusCodeError(500, message, cause); } } @@ -193,6 +217,11 @@ export class ErrorHelper { return statusNumber && statusNumber === 404; } + public static IsServerError(error: Error): boolean { + const statusNumber = ErrorHelper.GetStatus(error); + return statusNumber && statusNumber >= 500; + } + public static IsNotAuthorized(error: Error): boolean { const statusNumber = ErrorHelper.GetStatus(error); return statusNumber && statusNumber === 403; @@ -230,9 +259,6 @@ export class ErrorHelper { if (asAny?.statusCode && typeof asAny.statusCode === 'number') { return asAny.statusCode as number; } - if (asAny?.code && typeof asAny.code === 'number') { - return asAny.code as number; - } if (asAny?.status) { const status = asAny.status; const type = typeof status; @@ -245,6 +271,9 @@ export class ErrorHelper { return null; } } + if (asAny?.code && typeof asAny.code === 'number') { + return asAny.code as number; + } return null; } } @@ -272,11 +301,29 @@ export function stripDistFolderName(dirname: string) { return dirname; } +export function getSafeCosmosResourceKey(key: string) { + return key.replace(/[%:\\/?#]/g, ''); +} + export function sha256(str: string) { const hash = crypto.createHash('sha256').update(str).digest('base64'); return hash; } +export async function getThirdPartyLinkById( + linkProvider: ILinkProvider, + thirdPartyId: string | number +): Promise { + try { + return await linkProvider.getByThirdPartyId(String(thirdPartyId)); + } catch (error) { + if (ErrorHelper.IsNotFound(error)) { + return null; + } + throw error; + } +} + export interface ICustomizedNewRepositoryLogic { createContext(req: any): INewRepositoryContext; getAdditionalTelemetryProperties(context: INewRepositoryContext): IDictionary; diff --git a/utils.ts b/lib/utils.ts similarity index 90% rename from utils.ts rename to lib/utils.ts index 514b95476..d1935884c 100644 --- a/utils.ts +++ b/lib/utils.ts @@ -8,15 +8,19 @@ import fs from 'fs'; import path from 'path'; import { URL } from 'url'; import zlib from 'zlib'; -import { type Repository } from './business/repository'; -import { ReposAppRequest, IAppSession, IReposError, SiteConfiguration } from './interfaces'; +import { type Repository } from '../business/repository'; +import type { ReposAppRequest, IAppSession, IReposError, SiteConfiguration } from '../interfaces'; import { getProviders } from './transitional'; export function daysInMilliseconds(days: number): number { return 1000 * 60 * 60 * 24 * days; } +export function dateToDateString(date: Date) { + return date.toISOString().substr(0, 10); +} + export function stringOrNumberAsString(value: any) { if (typeof value === 'number') { return (value as number).toString(); @@ -262,7 +266,12 @@ export function writeTextToFile(filename: string, stringContent: string): Promis }); } -export function quitInTenSeconds(successful: boolean) { +export function quitInTenSeconds(successful: boolean, config?: SiteConfiguration) { + // To allow telemetry to flush, we'll wait typically + if (config?.debug?.exitImmediately || process.env.EXIT_IMMEDIATELY === '1') { + console.log(`EXIT_IMMEDIATELY set, exiting... exit code=${successful ? 0 : 1}`); + return process.exit(successful ? 0 : 1); + } console.log(`Quitting process in 10s... exit code=${successful ? 0 : 1}`); return setTimeout(() => { process.exit(successful ? 0 : 1); @@ -354,3 +363,24 @@ export function getDateTimeBasedBlobFolder() { } export const botBracket = '[bot]'; + +const githubAvatarHostnames = [ + 'githubusercontent.com', + 'objects.githubusercontent.com', + 'object.githubusercontent.com', + 'raw.githubusercontent.com', + 'avatars.githubusercontent.com', +]; + +export function getUserIdFromWellFormedAvatar(avatar: string): string { + // https://*.githubusercontent.com/u/userid?v=* + const url = new URL(avatar); + if (githubAvatarHostnames.includes(url.hostname)) { + const { pathname } = url; + const i = pathname.indexOf('/u/'); + if (i >= 0) { + return pathname.substr(i + 3); + } + } + return null; +} diff --git a/middleware/alternateApps.ts b/middleware/alternateApps.ts index 9f6ec74ae..76e783e49 100644 --- a/middleware/alternateApps.ts +++ b/middleware/alternateApps.ts @@ -8,13 +8,13 @@ const debug = Debug.debug('startup'); import path from 'path'; -import { IApplicationProfile, IReposApplication } from '../interfaces'; +import type { ApplicationProfile, IReposApplication, SiteConfiguration } from '../interfaces'; export default async function initializeAlternateApps( - config, + config: SiteConfiguration, app: IReposApplication, appName: string -): Promise { +): Promise { const appPath = path.resolve(path.join(__dirname, '..', appName, '/')); debug(`Alternate app requested: name=${appName}, path=${appPath}`); try { diff --git a/middleware/apiAad.ts b/middleware/apiAad.ts index a44c24dc1..6968e4e49 100644 --- a/middleware/apiAad.ts +++ b/middleware/apiAad.ts @@ -3,28 +3,30 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +import { NextFunction, Response } from 'express'; import jwt from 'jsonwebtoken'; import jwksClient from 'jwks-rsa'; import { isJsonError, jsonError } from './jsonError'; import { IApiRequest, wrapErrorForImmediateUserError } from './apiReposAuth'; -import { PersonalAccessToken } from '../entities/token/token'; -import { CreateError, getProviders } from '../transitional'; +import { PersonalAccessToken } from '../business/entities/token/token'; +import { CreateError, getProviders } from '../lib/transitional'; import getCompanySpecificDeployment from './companySpecificDeployment'; // CONSIDER: Caching of signing keys -export function requireAadApiAuthorizedScope(scope: string) { - return (req: IApiRequest, res, next) => { +export function requireAadApiAuthorizedScope(scope: string | string[]) { + return (req: IApiRequest, res: Response, next: NextFunction) => { const { apiKeyToken } = req; - if (!apiKeyToken.hasScope(scope)) { + const scopes = typeof scope === 'string' ? [scope] : scope; + if (!apiKeyToken.hasAnyScope(scopes)) { return next(jsonError(`Not authorized for ${scope}`, 403)); } return next(); }; } -export default function aadApiMiddleware(req: IApiRequest, res, next) { +export default function aadApiMiddleware(req: IApiRequest, res: Response, next: NextFunction) { return validateAadAuthorization(req) .then((ok) => { return next(); @@ -33,7 +35,7 @@ export default function aadApiMiddleware(req: IApiRequest, res, next) { if ((err as any).immediate === true) { console.warn(`AAD API authorization failed: ${err}`); } - return isJsonError(err) ? next(err) : jsonError(err, 500); + return isJsonError(err, req.url) ? next(err) : (jsonError(err, 500) as unknown); }); } @@ -121,20 +123,37 @@ async function validateAadAuthorization(req: IApiRequest): Promise { } const { appid, oid } = payload as any; - let approvedAppMonikerId = await aadApiValidator.getAuthorizedClientIdToken(appid); - if (!approvedAppMonikerId) { - approvedAppMonikerId = await aadApiValidator.getAuthorizedObjectIdToken(oid); + const monikerSources = []; + const approvedAppMonikerClientId = await aadApiValidator.getAuthorizedClientIdToken(appid); + if (approvedAppMonikerClientId) { + monikerSources.push('client'); + } + const approvedAppMonikerObjectId = await aadApiValidator.getAuthorizedObjectIdToken(oid); + if (approvedAppMonikerObjectId) { + monikerSources.push('object'); } - const notAuthorized = !approvedAppMonikerId; + const notAuthorized = !approvedAppMonikerClientId && !approvedAppMonikerObjectId; if (notAuthorized) { throw wrapErrorForImmediateUserError( jsonError(`App ${appid} and object ID ${oid} is not authorized for this API endpoint`, 403) ); } - const scopes = await aadApiValidator.getScopes(approvedAppMonikerId); - const displayValues = await aadApiValidator.getDisplayValues(approvedAppMonikerId); + const scopesSet = new Set(); + if (approvedAppMonikerClientId) { + const clientIdScopes = await aadApiValidator.getScopes(approvedAppMonikerClientId); + clientIdScopes.forEach((s) => scopesSet.add(s)); + } + if (approvedAppMonikerObjectId) { + const objectIdScopes = await aadApiValidator.getScopes(approvedAppMonikerObjectId); + objectIdScopes.forEach((s) => scopesSet.add(s)); + } + const scopes = Array.from(scopesSet); + + const displayValues = await aadApiValidator.getDisplayValues( + approvedAppMonikerClientId || approvedAppMonikerObjectId + ); const apiToken = PersonalAccessToken.CreateFromAadAuthorization( { @@ -151,6 +170,7 @@ async function validateAadAuthorization(req: IApiRequest): Promise { name: 'ApiAadAppAuthorized', properties: Object.assign({}, decodedToken as any, { authorizedScopes: scopes.join(','), + monikerSources: monikerSources.join(','), }), }); } catch (error) { diff --git a/middleware/apiReposAuth.ts b/middleware/apiReposAuth.ts index f6c17f786..ff26109de 100644 --- a/middleware/apiReposAuth.ts +++ b/middleware/apiReposAuth.ts @@ -11,10 +11,11 @@ import basicAuth from 'basic-auth'; import crypto from 'crypto'; +import { NextFunction, Response } from 'express'; import { jsonError } from './jsonError'; -import { getProviders } from '../transitional'; -import { PersonalAccessToken } from '../entities/token/token'; +import { getProviders } from '../lib/transitional'; +import { PersonalAccessToken } from '../business/entities/token/token'; import { ReposAppRequest } from '../interfaces'; export const wrapErrorForImmediateUserError = (err: Error) => { @@ -30,7 +31,7 @@ export interface IApiRequest extends ReposAppRequest { userContextOverwriteRequest?: any; // refactor? } -export default function ReposApiAuthentication(req: IApiRequest, res, next) { +export default function ReposApiAuthentication(req: IApiRequest, res: Response, next: NextFunction) { const user = basicAuth(req); const key = user ? user.pass || user.name : null; if (!key) { diff --git a/middleware/apiVstsAuth.ts b/middleware/apiVstsAuth.ts index 694e2af72..d34b6f67d 100644 --- a/middleware/apiVstsAuth.ts +++ b/middleware/apiVstsAuth.ts @@ -5,16 +5,17 @@ import axios from 'axios'; import asyncHandler from 'express-async-handler'; +import { NextFunction, Response } from 'express'; import { jsonError } from './jsonError'; import { IApiRequest } from './apiReposAuth'; -import { PersonalAccessToken } from '../entities/token/token'; -import { getProviders } from '../transitional'; +import { PersonalAccessToken } from '../business/entities/token/token'; +import { getProviders } from '../lib/transitional'; // TODO: consider better caching const localMemoryCacheVstsToAadId = new Map(); -const vstsAuth = asyncHandler(async (req: IApiRequest, res, next) => { +const vstsAuth = asyncHandler(async (req: IApiRequest, res: Response, next: NextFunction) => { const config = getProviders(req).config; if (!config) { return next(new Error('Missing configuration for the application')); diff --git a/middleware/appInsights.ts b/middleware/appInsights.ts index 11c01d0e8..f3c4051c0 100644 --- a/middleware/appInsights.ts +++ b/middleware/appInsights.ts @@ -3,13 +3,21 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +import { NextFunction, Response } from 'express'; + import wrapOrCreateInsightsConsoleClient from '../lib/insights'; import Debug from 'debug'; const debug = Debug.debug('startup'); import { setup as appInsightsSetup, defaultClient } from 'applicationinsights'; -import { IReposApplication, IProviders, ReposAppRequest } from '../interfaces'; +import type { + IReposApplication, + IProviders, + ReposAppRequest, + SiteConfiguration, + ExecutionEnvironment, +} from '../interfaces'; function ignoreKubernetesProbes(envelope /* , context */) { if ('RequestData' === envelope.data.baseType) { @@ -42,20 +50,22 @@ function filterTelemetry(envelope, context): boolean { return true; } -export default function initializeAppInsights(app: IReposApplication, config) { +export default function initializeAppInsights( + providers: IProviders, + executionEnvironment: ExecutionEnvironment, + app: IReposApplication, + config: SiteConfiguration +) { let client = undefined; if (!config) { // Configuration failure happened ahead of this module return; } - const providers = app.settings.providers as IProviders; let cs: string = config?.telemetry?.applicationInsightsConnectionString || config?.telemetry?.applicationInsightsKey; // Override the key with a job-specific one if this is a job execution instead - const jobCs: string = - config?.telemetry?.jobsApplicationInsightsConnectionString || - config?.telemetry?.jobsApplicationInsightsKey; - if (jobCs && config.isJobInternal === true) { + const jobCs: string = config?.telemetry?.jobsApplicationInsightsConnectionString; + if (jobCs && executionEnvironment.isJob === true) { cs = jobCs; } if (cs) { @@ -78,7 +88,7 @@ export default function initializeAppInsights(app: IReposApplication, config) { debug('insights telemetry is not configured with a key or connection string'); } - app.use((req: ReposAppRequest, res, next) => { + app?.use((req: ReposAppRequest, res: Response, next: NextFunction) => { // Acknowledge synthetic tests immediately without spending time in more middleware if ( req.headers && diff --git a/middleware/business/administration.ts b/middleware/business/administration.ts index 38e11a052..7b192cd09 100644 --- a/middleware/business/administration.ts +++ b/middleware/business/administration.ts @@ -3,14 +3,13 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; -const router: Router = Router(); +import { NextFunction, Response, Router } from 'express'; import { ReposAppRequest } from '../../interfaces'; -import { wrapError } from '../../utils'; +import { wrapError } from '../../lib/utils'; -function denyRoute(next) { +function denyRoute(next: NextFunction) { next( wrapError( null, @@ -20,7 +19,11 @@ function denyRoute(next) { ); } -export function requirePortalAdministrationPermission(req: ReposAppRequest, res, next) { +export function requirePortalAdministrationPermission( + req: ReposAppRequest, + res: Response, + next: NextFunction +) { req.individualContext .isPortalAdministrator() .then((isAdmin) => { diff --git a/middleware/business/allLinks.ts b/middleware/business/allLinks.ts index c25f57df3..ece49af9a 100644 --- a/middleware/business/allLinks.ts +++ b/middleware/business/allLinks.ts @@ -3,13 +3,15 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +import { NextFunction, Response } from 'express'; + import { ReposAppRequest } from '../../interfaces'; -import { getProviders } from '../../transitional'; -import { wrapError } from '../../utils'; +import { getProviders } from '../../lib/transitional'; +import { wrapError } from '../../lib/utils'; const cachedLinksRequestKeyName = 'cachedLinks'; -export async function ensureAllLinksInMemory(req: ReposAppRequest, res, next) { +export async function ensureAllLinksInMemory(req: ReposAppRequest, res: Response, next: NextFunction) { if (req[cachedLinksRequestKeyName]) { return next(); } diff --git a/middleware/business/authentication.ts b/middleware/business/authentication.ts index 2c371c112..b0efa15a7 100644 --- a/middleware/business/authentication.ts +++ b/middleware/business/authentication.ts @@ -3,30 +3,32 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +import { Response, NextFunction } from 'express'; + import { ReposAppRequest, IAppSession } from '../../interfaces'; import Debug from 'debug'; const debug = Debug.debug('user'); -import { getProviders } from '../../transitional'; +import { getProviders } from '../../lib/transitional'; import { ICorporateIdentity, IGitHubIdentity, IndividualContext, GitHubIdentitySource, } from '../../business/user'; -import { storeOriginalUrlAsReferrer } from '../../utils'; +import { storeOriginalUrlAsReferrer } from '../../lib/utils'; import getCompanySpecificDeployment from '../companySpecificDeployment'; export async function requireAuthenticatedUserOrSignInExcluding( exclusionPaths: string[], req: ReposAppRequest, - res, - next + res: Response, + next: NextFunction ) { - const baseUrl = req.baseUrl; + const url = req.url; for (let i = 0; i < exclusionPaths.length; i++) { - if (baseUrl.startsWith(exclusionPaths[i])) { + if (url.startsWith(exclusionPaths[i])) { console.log(`${req.method} ${req.baseUrl} excluded from auth by prefix: ${exclusionPaths[i]}`); return next(); } @@ -34,7 +36,7 @@ export async function requireAuthenticatedUserOrSignInExcluding( return await requireAuthenticatedUserOrSignIn(req, res, next); } -export async function requireAccessTokenClient(req: ReposAppRequest, res, next) { +export async function requireAccessTokenClient(req: ReposAppRequest, res: Response, next: NextFunction) { if (req.oauthAccessToken) { return next(); } @@ -63,7 +65,7 @@ export async function requireAccessTokenClient(req: ReposAppRequest, res, next) return next(); } -function signoutThenSignIn(req: ReposAppRequest, res) { +function signoutThenSignIn(req: ReposAppRequest, res: Response) { const { insights } = getProviders(req); req.logout({ keepSessionInfo: false }, (err) => { if (err) { @@ -73,7 +75,7 @@ function signoutThenSignIn(req: ReposAppRequest, res) { }); } -function redirectToSignIn(req, res) { +function redirectToSignIn(req: ReposAppRequest, res: Response) { const config = getProviders(req).config; storeOriginalUrlAsReferrer( req, @@ -83,7 +85,11 @@ function redirectToSignIn(req, res) { ); } -export async function requireAuthenticatedUserOrSignIn(req: ReposAppRequest, res, next) { +export async function requireAuthenticatedUserOrSignIn( + req: ReposAppRequest, + res: Response, + next: NextFunction +) { const companySpecific = getCompanySpecificDeployment(); const providers = getProviders(req); const { config } = providers; @@ -111,7 +117,7 @@ export async function requireAuthenticatedUserOrSignIn(req: ReposAppRequest, res return shouldRedirectToSignIn ? redirectToSignIn(req, res) : next(); } -export function setIdentity(req: ReposAppRequest, res, next) { +export function setIdentity(req: ReposAppRequest, res: Response, next: NextFunction) { const activeContext = (req.individualContext || req.apiContext) as IndividualContext; if (!activeContext) { return next(new Error('No context available')); diff --git a/middleware/business/corporateAdministrators.ts b/middleware/business/corporateAdministrators.ts index b36e9b391..07e2f5795 100644 --- a/middleware/business/corporateAdministrators.ts +++ b/middleware/business/corporateAdministrators.ts @@ -6,17 +6,19 @@ // This route does not use GitHub as a source of truth but instead falls back to // corporate assigned usernames or security group membership. +import { NextFunction, Response } from 'express'; + import { ReposAppRequest } from '../../interfaces'; -import { getProviders } from '../../transitional'; +import { getProviders } from '../../lib/transitional'; import { IndividualContext } from '../../business/user'; -import { wrapError } from '../../utils'; +import { wrapError } from '../../lib/utils'; import { jsonError } from '../jsonError'; export interface IReposAppRequestWithSystemAdministration extends ReposAppRequest { isSystemAdministrator: boolean; } -function denyRoute(next, isApi: boolean) { +function denyRoute(next: NextFunction, isApi: boolean) { if (isApi) { return next(jsonError('This API is unavailable for you', 403)); } @@ -29,7 +31,11 @@ function denyRoute(next, isApi: boolean) { ); } -export async function AuthorizeOnlyCorporateAdministrators(req: ReposAppRequest, res, next) { +export async function AuthorizeOnlyCorporateAdministrators( + req: ReposAppRequest, + res: Response, + next: NextFunction +) { const { operations } = getProviders(req); const activeContext = (req.individualContext || req.apiContext) as IndividualContext; const corporateId = activeContext.corporateIdentity?.id; @@ -42,8 +48,8 @@ export async function AuthorizeOnlyCorporateAdministrators(req: ReposAppRequest, export async function checkIsCorporateAdministrator( req: IReposAppRequestWithSystemAdministration, - res, - next + res: Response, + next: NextFunction ) { await getIsCorporateAdministrator(req); return next(); diff --git a/middleware/business/corporateAlias.ts b/middleware/business/corporateAlias.ts index 7221975ff..78b451f82 100644 --- a/middleware/business/corporateAlias.ts +++ b/middleware/business/corporateAlias.ts @@ -5,7 +5,7 @@ import { jsonError } from '..'; import { IProviders, ReposAppRequest } from '../../interfaces'; -import { getProviders } from '../../transitional'; +import { getProviders } from '../../lib/transitional'; import { IndividualContext } from '../../business/user'; const cachedCorporateAliasRequestKey = '__corporateAlias'; diff --git a/middleware/business/corporateHierarchy.ts b/middleware/business/corporateHierarchy.ts index 77b0e40fc..ed38c588d 100644 --- a/middleware/business/corporateHierarchy.ts +++ b/middleware/business/corporateHierarchy.ts @@ -6,7 +6,7 @@ import { jsonError } from '..'; import { IProviders, ReposAppRequest } from '../../interfaces'; import { IGraphEntry } from '../../lib/graphProvider'; -import { getProviders } from '../../transitional'; +import { getProviders } from '../../lib/transitional'; import { IndividualContext } from '../../business/user'; const cachedCorporateHierarchyRequestKey = '__corporateTree'; diff --git a/middleware/business/corporateMail.ts b/middleware/business/corporateMail.ts index fd6a2a5ab..72b294801 100644 --- a/middleware/business/corporateMail.ts +++ b/middleware/business/corporateMail.ts @@ -5,7 +5,7 @@ import { jsonError } from '..'; import { IProviders, ReposAppRequest } from '../../interfaces'; -import { getProviders } from '../../transitional'; +import { ErrorHelper, getProviders } from '../../lib/transitional'; import { IndividualContext } from '../../business/user'; const cachedCorporateMailRequestKey = '__corporateMail'; @@ -25,18 +25,31 @@ export async function getCorporateMailFromActiveContext( providers: IProviders, activeContext: IndividualContext ): Promise { - const { graphProvider } = providers; if (!activeContext.corporateIdentity || !activeContext.corporateIdentity.id) { throw jsonError('No corporate identity', 401); } let corporateMail = activeContext?.link?.corporateMailAddress; if (!corporateMail) { - const id = activeContext.corporateIdentity.id; - const entry = await graphProvider.getUserById(id); - if (!entry || !entry.mailNickname) { + const mail = await tryGetCorporateMailFromId(providers, activeContext.corporateIdentity.id); + if (!mail) { throw jsonError('Invalid corporate identity', 401); } - corporateMail = entry.mail; + corporateMail = mail; } return corporateMail; } + +export async function tryGetCorporateMailFromId(providers: IProviders, corporateId: string): Promise { + const { graphProvider } = providers; + try { + const info = await graphProvider.getUserById(corporateId); + if (info?.mail) { + return info.mail; + } + } catch (error) { + if (!ErrorHelper.IsNotFound(error)) { + throw error; + } + } + return null; +} diff --git a/middleware/business/employeesOnly.ts b/middleware/business/employeesOnly.ts index ed6bb588f..3689147a4 100644 --- a/middleware/business/employeesOnly.ts +++ b/middleware/business/employeesOnly.ts @@ -5,9 +5,15 @@ // This is a Microsoft-specific piece of middleware. +import { NextFunction, Response } from 'express'; + import { ReposAppRequest } from '../../interfaces'; -export function AuthorizeOnlyFullTimeEmployeesAndInterns(req: ReposAppRequest, res, next) { +export function AuthorizeOnlyFullTimeEmployeesAndInterns( + req: ReposAppRequest, + res: Response, + next: NextFunction +) { const individualContext = req.individualContext; if (!individualContext.corporateIdentity || !individualContext.corporateIdentity.username) { return next(new Error('This resource is only available to authenticated users.')); diff --git a/middleware/links/index.ts b/middleware/business/links.ts similarity index 90% rename from middleware/links/index.ts rename to middleware/business/links.ts index b0f24d8e1..2ff7e2a68 100644 --- a/middleware/links/index.ts +++ b/middleware/business/links.ts @@ -6,19 +6,25 @@ // Within the context, tries to resolve a link if it _can_. It does not force that a user is linked! import { IndividualContext } from '../../business/user'; -import { getProviders } from '../../transitional'; -import { wrapError } from '../../utils'; +import { getProviders } from '../../lib/transitional'; +import { wrapError } from '../../lib/utils'; import { ReposAppRequest, IReposError } from '../../interfaces'; +import { NextFunction, Response } from 'express'; export function RequireLinkMatchesGitHubSessionExceptPrefixedRoute(prefix: string) { return requireLinkMatchesGitHubSession.bind(null, prefix); } -export function RequireLinkMatchesGitHubSession(req: ReposAppRequest, res, next) { +export function RequireLinkMatchesGitHubSession(req: ReposAppRequest, res: Response, next: NextFunction) { return requireLinkMatchesGitHubSession(null, req, res, next); } -function requireLinkMatchesGitHubSession(allowedPrefix: string, req: ReposAppRequest, res, next) { +function requireLinkMatchesGitHubSession( + allowedPrefix: string, + req: ReposAppRequest, + res: Response, + next: NextFunction +) { // trying to be equivalent to legacy code in ./usernameConsistency (lightweight) const context = req.individualContext; if (!context) { @@ -67,7 +73,7 @@ function requireLinkMatchesGitHubSession(allowedPrefix: string, req: ReposAppReq return next(securityError); } -export async function AddLinkToRequest(req, res, next) { +export async function AddLinkToRequest(req, res: Response, next: NextFunction) { const activeContext = (req.individualContext || req.apiContext) as IndividualContext; const contextName = req.individualContext ? 'Individual User Context' : 'API Context'; if (!activeContext) { diff --git a/middleware/business/organization.ts b/middleware/business/organization.ts index 04ff3a199..b5872dc00 100644 --- a/middleware/business/organization.ts +++ b/middleware/business/organization.ts @@ -3,8 +3,12 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +import { NextFunction, Response } from 'express'; + import { ReposAppRequest } from '../../interfaces/web'; import { jsonError } from '../jsonError'; +import { CreateError, ErrorHelper, getProviders } from '../../lib/transitional'; +import { setOrganizationProfileForRequest } from '../github/ensureOrganizationProfile'; export enum OrganizationManagementType { Managed = 'managed', @@ -21,7 +25,11 @@ export function getOrganizationManagementType(req: IReposAppRequestWithOrganizat return req.organizationManagementType; } -export function blockIfUnmanagedOrganization(req: IReposAppRequestWithOrganizationManagementType, res, next) { +export function blockIfUnmanagedOrganization( + req: IReposAppRequestWithOrganizationManagementType, + res: Response, + next: NextFunction +) { const managementType = getOrganizationManagementType(req); switch (managementType) { case OrganizationManagementType.Unmanaged: @@ -32,3 +40,38 @@ export function blockIfUnmanagedOrganization(req: IReposAppRequestWithOrganizati return next(jsonError('unknown organization management type', 500)); } } + +export async function apiMiddlewareOrganizationsToOrganization( + req: IReposAppRequestWithOrganizationManagementType, + res: Response, + next: NextFunction +) { + const { operations } = getProviders(req); + const { orgName } = req.params; + req.organizationName = orgName; + try { + const org = operations.getOrganization(orgName); + if (org) { + req.organizationManagementType = OrganizationManagementType.Managed; + req.organization = org; + return next(); + } + } catch (orgNotFoundError) { + if (!ErrorHelper.IsNotFound(orgNotFoundError)) { + return next(orgNotFoundError); + } + } + try { + const org = operations.getUncontrolledOrganization(orgName); + req.organizationManagementType = OrganizationManagementType.Unmanaged; + req.organization = org; + await setOrganizationProfileForRequest(req); + } catch (orgProfileError) { + if (ErrorHelper.IsNotFound(orgProfileError)) { + return next(CreateError.NotFound(`The organization ${orgName} does not exist`)); + } else { + return next(orgProfileError); + } + } + return next(); +} diff --git a/middleware/business/repository.ts b/middleware/business/repository.ts new file mode 100644 index 000000000..d93618313 --- /dev/null +++ b/middleware/business/repository.ts @@ -0,0 +1,34 @@ +// +// Copyright (c) Microsoft. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +import { NextFunction, Response } from 'express'; + +import { Repository } from '../../business/repository'; +import type { ReposAppRequest } from '../../interfaces/web'; +import { CreateError } from '../../lib/transitional'; + +export type RequestWithRepo = ReposAppRequest & { + repository: Repository; +}; + +export async function apiMiddlewareRepositoriesToRepository( + req: RequestWithRepo, + res: Response, + next: NextFunction +) { + const { organization } = req; + if (!organization) { + return next(CreateError.InvalidParameters('No organization instance available')); + } + + const { repoName } = req.params; + if (!repoName) { + return next(CreateError.InvalidParameters('No repository name provided')); + } + + // does not confirm the name + req.repository = organization.repository(repoName); + return next(); +} diff --git a/middleware/business/setContext.ts b/middleware/business/setContext.ts index 9b2d90794..37d8a34e2 100644 --- a/middleware/business/setContext.ts +++ b/middleware/business/setContext.ts @@ -3,6 +3,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +import { NextFunction, Response } from 'express'; import { IndividualContext, IIndividualContextOptions, @@ -11,9 +12,10 @@ import { SessionUserProperties, WebApiContext, } from '../../business/user'; -import { getProviders } from '../../transitional'; +import { ReposAppRequest } from '../../interfaces'; +import { getProviders } from '../../lib/transitional'; -export function webContextMiddleware(req, res, next) { +export function webContextMiddleware(req: ReposAppRequest, res: Response, next: NextFunction) { const { operations, insights } = getProviders(req); if (req.apiContext) { const msg = 'INVALID: API and web contexts should not be mixed'; @@ -46,7 +48,7 @@ export function webContextMiddleware(req, res, next) { return next(); } -export function apiContextMiddleware(req, res, next) { +export function apiContextMiddleware(req, res: Response, next: NextFunction) { const { operations, insights } = getProviders(req); if (req.individualContext) { const msg = 'INVALID: API and web contexts should not be mixed'; diff --git a/middleware/business/userSettings.ts b/middleware/business/userSettings.ts new file mode 100644 index 000000000..8ab028405 --- /dev/null +++ b/middleware/business/userSettings.ts @@ -0,0 +1,41 @@ +// +// Copyright (c) Microsoft. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +import type { NextFunction, Response } from 'express'; + +import type { ReposAppRequestWithUserSettings } from '../../interfaces/middleware'; + +import { UserSettings } from '../../business/entities/userSettings'; +import { ErrorHelper, getProviders } from '../../lib/transitional'; +import { IndividualContext } from '../../business/user'; + +export async function getUserSettings( + req: ReposAppRequestWithUserSettings, + res: Response, + next: NextFunction +) { + const activeContext = (req.individualContext || req.apiContext) as IndividualContext; + const corporateId = activeContext?.corporateIdentity?.id; + const { userSettingsProvider } = getProviders(req); + if (corporateId && !req.userSettings) { + let settings: UserSettings = null; + try { + settings = await userSettingsProvider.getUserSettings(corporateId); + } catch (notFoundError) { + if (ErrorHelper.IsNotFound(notFoundError)) { + // ignore + } else { + throw notFoundError; + } + } + if (!settings) { + settings = new UserSettings(); + settings.corporateId = corporateId; + await userSettingsProvider.insertUserSettings(settings); + } + req.userSettings = settings; + } + return next(); +} diff --git a/middleware/campaign.ts b/middleware/campaign.ts index f1278f930..2d8fd77da 100644 --- a/middleware/campaign.ts +++ b/middleware/campaign.ts @@ -3,7 +3,9 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { getProviders } from '../transitional'; +import { NextFunction, Response } from 'express'; + +import { getProviders } from '../lib/transitional'; interface ICampaignData { uri?: any; @@ -23,7 +25,7 @@ export default function initializeCampaigns(app) { // come through the app. app.use('*', campaignMiddleware); - function campaignMiddleware(req, res, next) { + function campaignMiddleware(req, res: Response, next: NextFunction) { process.nextTick(processCampaignTelemetry.bind(null, req)); // Immediate return to keep middleware going diff --git a/middleware/codespaces.ts b/middleware/codespaces.ts index 46e4e9c00..4a38fc2c1 100644 --- a/middleware/codespaces.ts +++ b/middleware/codespaces.ts @@ -3,13 +3,15 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +import { NextFunction, Response } from 'express'; + import { ReposAppRequest } from '../interfaces'; // Assistant for when using Visual Studio Code to connect to a Codespace // locally instead of the web. The default port forwarding experience is // to toast the user to browse to 127.0.0.1:3000, but since AAD does not // allow for IP-based callback URLs, the user must use localhost. -export function codespacesDevAssistant(req: ReposAppRequest, res, next) { +export function codespacesDevAssistant(req: ReposAppRequest, res: Response, next: NextFunction) { if (req.hostname === '127.0.0.1') { console.warn( `${req.method} ${req.url}: WARNING: You're trying to connect to ${req.hostname} from your codespace.` diff --git a/middleware/companySpecificDeployment.ts b/middleware/companySpecificDeployment.ts index d42009bff..14d38df2a 100644 --- a/middleware/companySpecificDeployment.ts +++ b/middleware/companySpecificDeployment.ts @@ -18,13 +18,13 @@ function getCompanySpecificDeploymentName() { } function getCompanySpecificDeployment(): ICompanySpecificStartup { + if (instance) { + return instance; + } const name = getCompanySpecificDeploymentName(); if (!name) { return null; } - if (instance) { - return instance; - } try { const pn = path.join(__dirname, '..', name); const dynamicInclude = require(pn); diff --git a/middleware/corporateViews.ts b/middleware/corporateViews.ts index 4f16ef7f0..24b53f8bb 100644 --- a/middleware/corporateViews.ts +++ b/middleware/corporateViews.ts @@ -7,7 +7,7 @@ import { constants as fsConstants, promises as fs } from 'fs'; import path from 'path'; import { IProviders } from '../interfaces'; -import { stripDistFolderName } from '../transitional'; +import { stripDistFolderName } from '../lib/transitional'; // providers.corporateViews: // --- diff --git a/middleware/correlationId.ts b/middleware/correlationId.ts index df5b433a7..9f491d1a5 100644 --- a/middleware/correlationId.ts +++ b/middleware/correlationId.ts @@ -3,10 +3,15 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +import { NextFunction, Request, Response } from 'express'; import { randomUUID } from 'crypto'; +export type WithCorrelationId = T & { + correlationId?: string; +}; + // Generate a correlation ID -export default function (req, res, next) { +export default function (req: WithCorrelationId, res: Response, next: NextFunction) { req.correlationId = randomUUID(); return next(); } diff --git a/middleware/errorHandler.ts b/middleware/errorHandler.ts index 7277cc666..dcfd64194 100644 --- a/middleware/errorHandler.ts +++ b/middleware/errorHandler.ts @@ -6,9 +6,11 @@ import querystring from 'querystring'; import { AxiosError } from 'axios'; -import { wrapError } from '../utils'; -import { getProviders } from '../transitional'; +import { wrapError } from '../lib/utils'; +import { getProviders } from '../lib/transitional'; import { isJsonError } from '.'; +import { NextFunction, Response } from 'express'; +import { ReposAppRequest } from '../interfaces'; function redactRootPathsFromString(string, path) { if (typeof string === 'string' && string.includes && string.split) { @@ -49,9 +51,16 @@ const exceptionFieldsOfInterest = [ 'innerMessage', ]; -export default function SiteErrorHandler(err, req, res, next) { +export default function SiteErrorHandler( + error: unknown, + req: ReposAppRequest, + res: Response, + next: NextFunction +) { + let err = error as any; + const isJson = isJsonError(err, req.url); // CONSIDER: Let's eventually decouple all of our error message improvements to another area to keep the error handler intact. - const { applicationProfile, config } = getProviders(req); + const { applicationProfile, config, insights } = getProviders(req); const correlationId = req.correlationId; const errorStatus = err ? err.status || err.statusCode : undefined; // Per GitHub: https://developer.github.com/v3/oauth/#bad-verification-code @@ -63,8 +72,8 @@ export default function SiteErrorHandler(err, req, res, next) { err.oauthError.message === 'The code passed is incorrect or expired.')) && req.scrubbedUrl.startsWith('/auth/github/') ) { - req.insights.trackMetric({ name: 'GitHubInvalidExpiredCodeRedirect', value: 1 }); - req.insights.trackEvent({ name: 'GitHubInvalidExpiredCodeRetry' }); + insights?.trackMetric({ name: 'GitHubInvalidExpiredCodeRedirect', value: 1 }); + insights?.trackEvent({ name: 'GitHubInvalidExpiredCodeRetry' }); return res.redirect( req.scrubbedUrl === '/auth/github/callback/increased-scope?code=*****' ? '/auth/github/increased-scope' @@ -74,7 +83,7 @@ export default function SiteErrorHandler(err, req, res, next) { const isGitHubAbuseRateLimit = err && err.message && err.message.includes && err.message.includes('#abuse-rate-limits'); if (isGitHubAbuseRateLimit) { - req.insights.trackMetric({ name: 'GitHubAbuseRateLimit', value: 1 }); + insights?.trackMetric({ name: 'GitHubAbuseRateLimit', value: 1 }); } if ( err.message && @@ -82,8 +91,8 @@ export default function SiteErrorHandler(err, req, res, next) { err.message.includes('ETIMEDOUT') && (err.message.includes('192.30.253.116') || err.message.includes('192.30.253.117')) ) { - req.insights.trackMetric({ name: 'GitHubApiTimeout', value: 1 }); - req.insights.trackEvent({ name: 'GitHubApiTimeout' }); + insights?.trackMetric({ name: 'GitHubApiTimeout', value: 1 }); + insights?.trackEvent({ name: 'GitHubApiTimeout' }); err = wrapError(err, 'The GitHub API is temporarily down. Please try again soon.', false); } let primaryUserInstance = req.user ? req.user.github : null; @@ -103,7 +112,7 @@ export default function SiteErrorHandler(err, req, res, next) { stk: undefined, message: undefined, }; - if (req.insights && req.insights.trackException) { + if (insights?.trackException) { for (let i = 0; err && i < exceptionFieldsOfInterest.length; i++) { const key = exceptionFieldsOfInterest[i]; const value = err[key]; @@ -125,7 +134,7 @@ export default function SiteErrorHandler(err, req, res, next) { } if (isGitHubAbuseRateLimit) { insightsProperties.message = err.message; - req.insights.trackEvent({ + insights?.trackEvent({ name: 'GitHubAbuseRateLimitError', properties: insightsProperties, }); @@ -133,7 +142,7 @@ export default function SiteErrorHandler(err, req, res, next) { if (err && err['json']) { // not tracking jsonErrors for now, they pollute app insights } else { - req.insights.trackException({ exception: err, properties: insightsProperties }); + insights?.trackException({ exception: err, properties: insightsProperties }); } } } @@ -141,7 +150,6 @@ export default function SiteErrorHandler(err, req, res, next) { } if (err !== undefined && err.skipLog !== true) { console.log('Error: ' + (err && err.message ? err.message : 'Error is undefined.')); - const isJson = isJsonError(err); if (err.stack && !isJson) { console.error(err.stack); } @@ -234,7 +242,7 @@ export default function SiteErrorHandler(err, req, res, next) { // Support JSON-based error display for the API route, showing just a small // subset of typical view properties to share from the error instance. - if (err && err.json === true) { + if (isJson) { const safeError = { message: safeMessage, correlationId: correlationId, diff --git a/middleware/error-routes.ts b/middleware/errorRoutes.ts similarity index 74% rename from middleware/error-routes.ts rename to middleware/errorRoutes.ts index f5f621d02..f0b92c709 100644 --- a/middleware/error-routes.ts +++ b/middleware/errorRoutes.ts @@ -3,10 +3,15 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +import { NextFunction, Request, Response } from 'express'; + import { IReposApplication, IReposError } from '../interfaces'; -import RouteErrorHandler from './errorHandler'; +import routeErrorHandler from './errorHandler'; export default async function configureErrorRoutes(app: IReposApplication, initializationError: Error) { + if (!app) { + return; + } if (initializationError) { console.warn('Initialization Error Present: All app requests will fail!'); @@ -14,7 +19,7 @@ export default async function configureErrorRoutes(app: IReposApplication, initi // for any request. Should evaluate whether to hide for // production scenarios or if there is a risk of the // error message leaking sensitive data. - app.use((req, res, next) => { + app.use((req: Request, res: Response, next: NextFunction) => { const error: IReposError = new Error('Application initialization error', { cause: initializationError, }); @@ -23,12 +28,12 @@ export default async function configureErrorRoutes(app: IReposApplication, initi }); } - app.use(function (req, res, next) { + app.use(function (req: Request, res: Response, next: NextFunction) { const err: IReposError = new Error('Not Found'); err.status = 404; err.skipLog = true; - next(err); + return next(err); }); - app.use(RouteErrorHandler); + app.use(routeErrorHandler); } diff --git a/middleware/github/blockEnterpriseManagedUsers.ts b/middleware/github/blockEnterpriseManagedUsers.ts index 740d11dad..b9d13c50f 100644 --- a/middleware/github/blockEnterpriseManagedUsers.ts +++ b/middleware/github/blockEnterpriseManagedUsers.ts @@ -3,10 +3,16 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +import { NextFunction, Response } from 'express'; + import { ReposAppRequest, IReposError } from '../../interfaces'; -import { isEnterpriseManagedUserLogin } from '../../utils'; +import { isEnterpriseManagedUserLogin } from '../../lib/utils'; -export function blockEnterpriseManagedUsersAuthentication(req: ReposAppRequest, res, next) { +export function blockEnterpriseManagedUsersAuthentication( + req: ReposAppRequest, + res: Response, + next: NextFunction +) { const context = req.individualContext; if (!context) { return next(new Error('Missing context')); diff --git a/middleware/github/ensureOrganizationProfile.ts b/middleware/github/ensureOrganizationProfile.ts new file mode 100644 index 000000000..7dbec0828 --- /dev/null +++ b/middleware/github/ensureOrganizationProfile.ts @@ -0,0 +1,57 @@ +// +// Copyright (c) Microsoft. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +import asyncHandler from 'express-async-handler'; +import memoryCache from 'memory-cache'; +import { NextFunction, Response } from 'express'; + +import type { IReposAppRequestWithOrganizationManagementType } from '../business/organization'; +import { CreateError, getProviders } from '../../lib/transitional'; +import { + getOrganizationDetailsSanitized, + type GitHubOrganizationResponseSanitized, +} from '../../business/organization'; +import type { IProviders } from '../../interfaces'; + +export async function setOrganizationProfileForRequest(req: IReposAppRequestWithOrganizationManagementType) { + const providers = getProviders(req); + const org = req.organization; + if (!org) { + throw CreateError.InvalidParameters('No organization instance available in the request'); + } + if (!req.organizationProfile && org?.id) { + req.organizationProfile = await getOrganizationProfileViaMemoryCache(providers, String(org.id)); + } + if (!req.organizationProfile && org) { + req.organizationProfile = getOrganizationDetailsSanitized(await org.getDetails()); + } +} + +export async function getOrganizationProfileViaMemoryCache(providers: IProviders, organizationId: string) { + const { operations } = providers; + const cacheTimeMs = 1000 * 60 * 60 * 24; + + const key = `org:profile:${organizationId}`; + let profile = memoryCache.get(key) as GitHubOrganizationResponseSanitized; + if (!profile) { + const details = getOrganizationDetailsSanitized( + await operations.getOrganizationProfileById(Number(organizationId)) + ); + profile = details; + memoryCache.put(key, details, cacheTimeMs); + } + return profile; +} + +async function ensureOrganizationProfile( + req: IReposAppRequestWithOrganizationManagementType, + res: Response, + next: NextFunction +) { + await setOrganizationProfileForRequest(req); + return next(); +} + +export const ensureOrganizationProfileMiddleware = asyncHandler(ensureOrganizationProfile); diff --git a/middleware/github/orgPermissions.ts b/middleware/github/orgPermissions.ts index 9a0626211..d3a4a63a0 100644 --- a/middleware/github/orgPermissions.ts +++ b/middleware/github/orgPermissions.ts @@ -3,8 +3,10 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +import { NextFunction, Response } from 'express'; + import { OrganizationMembershipState, ReposAppRequest } from '../../interfaces'; -import { wrapError } from '../../utils'; +import { wrapError } from '../../lib/utils'; const orgPermissionsCacheKeyName = 'orgPermissions'; const orgOwnersCacheKeyName = 'orgOwners'; @@ -21,7 +23,11 @@ export function GetOrganizationPermissionsFromRequest(req: ReposAppRequest) { return req[orgPermissionsCacheKeyName]; } -export async function AddOrganizationPermissionsToRequest(req: ReposAppRequest, res, next) { +export async function AddOrganizationPermissionsToRequest( + req: ReposAppRequest, + res: Response, + next: NextFunction +) { // Only compute once per request if (req[orgPermissionsCacheKeyName]) { return next(); diff --git a/middleware/github/repoPermissions.ts b/middleware/github/repoPermissions.ts index 735f76bc1..d6c06a63a 100644 --- a/middleware/github/repoPermissions.ts +++ b/middleware/github/repoPermissions.ts @@ -3,7 +3,9 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { ErrorHelper, getProviders } from '../../transitional'; +import { NextFunction, Response } from 'express'; + +import { ErrorHelper, getProviders } from '../../lib/transitional'; import { Repository } from '../../business/repository'; import { GitHubIdentitySource, IIndividualContextOptions, IndividualContext } from '../../business/user'; import getCompanySpecificDeployment from '../companySpecificDeployment'; @@ -107,30 +109,33 @@ export async function getComputedRepositoryPermissions( repoPermissions, activeContext ); - if (!activeContext.link) { - return repoPermissions; - } - repoPermissions.isLinked = true; - const login = activeContext.getGitHubIdentity().username; - const organization = repository.organization; - const isSudoer = await organization.isSudoer(login, activeContext.link); const isPortalSudoer = await activeContext.isPortalAdministrator(); - if (isSudoer === true || isPortalSudoer === true) { + if (isPortalSudoer) { repoPermissions.sudo = true; } - try { - const collaborator = await repository.getCollaborator(login); - if (collaborator) { - if (collaborator.permission === GitHubCollaboratorPermissionLevel.Admin) { - repoPermissions.admin = repoPermissions.read = repoPermissions.write = true; - } else if (collaborator.permission === GitHubCollaboratorPermissionLevel.Write) { - repoPermissions.read = repoPermissions.write = true; - } else if (collaborator.permission === GitHubCollaboratorPermissionLevel.Read) { - repoPermissions.read = true; + repoPermissions.isLinked = !!activeContext.link; + const hasGitHubIdentity = !!activeContext?.getGitHubIdentity()?.username; + if (hasGitHubIdentity) { + const login = activeContext.getGitHubIdentity().username; + const organization = repository.organization; + const isSudoer = await organization.isSudoer(login, activeContext.link); + if (isSudoer) { + repoPermissions.sudo = true; + } + try { + const collaborator = await repository.getCollaborator(login); + if (collaborator) { + if (collaborator.permission === GitHubCollaboratorPermissionLevel.Admin) { + repoPermissions.admin = repoPermissions.read = repoPermissions.write = true; + } else if (collaborator.permission === GitHubCollaboratorPermissionLevel.Write) { + repoPermissions.read = repoPermissions.write = true; + } else if (collaborator.permission === GitHubCollaboratorPermissionLevel.Read) { + repoPermissions.read = true; + } } + } catch (getCollaboratorPermissionError) { + console.dir(getCollaboratorPermissionError); } - } catch (getCollaboratorPermissionError) { - console.dir(getCollaboratorPermissionError); } if (repoPermissions.admin || repoPermissions.sudo) { repoPermissions.allowAdministration = true; @@ -145,7 +150,11 @@ export async function getComputedRepositoryPermissions( return repoPermissions; } -export async function AddRepositoryPermissionsToRequest(req: ReposAppRequest, res, next) { +export async function AddRepositoryPermissionsToRequest( + req: ReposAppRequest, + res: Response, + next: NextFunction +) { if (req[repoPermissionsCacheKeyName]) { return next(); } diff --git a/middleware/github/requireActiveSession.ts b/middleware/github/requireActiveSession.ts index f58ac23b6..a0b46b553 100644 --- a/middleware/github/requireActiveSession.ts +++ b/middleware/github/requireActiveSession.ts @@ -3,11 +3,12 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +import { NextFunction, Response } from 'express'; import { ReposAppRequest } from '../../interfaces'; -import { getProviders } from '../../transitional'; -import { isCodespacesAuthenticating, storeOriginalUrlAsReferrer } from '../../utils'; +import { getProviders } from '../../lib/transitional'; +import { isCodespacesAuthenticating, storeOriginalUrlAsReferrer } from '../../lib/utils'; -export default function RequireActiveGitHubSession(req: ReposAppRequest, res, next) { +export default function RequireActiveGitHubSession(req: ReposAppRequest, res: Response, next: NextFunction) { const { config } = getProviders(req); const identity = req.individualContext.getSessionBasedGitHubIdentity(); if (!identity) { diff --git a/middleware/github/systemWidePermissions.ts b/middleware/github/systemWidePermissions.ts index 16d6d95cf..8787fbdc1 100644 --- a/middleware/github/systemWidePermissions.ts +++ b/middleware/github/systemWidePermissions.ts @@ -3,11 +3,17 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +import { NextFunction, Response } from 'express'; + import { ReposAppRequest } from '../../interfaces'; const requestCachedKeyName = 'systemWidePermissions'; -export default function addSystemWidePermissionsToRequest(req: ReposAppRequest, res, next) { +export default function addSystemWidePermissionsToRequest( + req: ReposAppRequest, + res: Response, + next: NextFunction +) { // Only compute once per request if (req[requestCachedKeyName]) { return next(); diff --git a/middleware/github/teamPermissions.ts b/middleware/github/teamPermissions.ts index 16c7317f0..0061260e5 100644 --- a/middleware/github/teamPermissions.ts +++ b/middleware/github/teamPermissions.ts @@ -3,14 +3,17 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +import { NextFunction, Response } from 'express'; + import { Team } from '../../business'; import { GitHubTeamRole, ITeamMembershipRoleState, + NoCacheNoBackground, OrganizationMembershipState, ReposAppRequest, } from '../../interfaces'; -import { getProviders } from '../../transitional'; +import { getProviders } from '../../lib/transitional'; import { IndividualContext } from '../../business/user'; import getCompanySpecificDeployment from '../companySpecificDeployment'; @@ -40,10 +43,11 @@ export function getTeamMembershipFromRequest(req: ReposAppRequest) { return req[teamStatusCacheKeyName] as IRequestTeamMembershipStatus; } -export async function AddTeamMembershipToRequest(req: ReposAppRequest, res, next) { +export async function AddTeamMembershipToRequest(req: ReposAppRequest, res: Response, next: NextFunction) { if (req[teamStatusCacheKeyName]) { return next(); } + const skipCache = req.query.cache === '0'; const team2 = req['team2'] as Team; if (!team2) { return next(new Error('team2 required')); @@ -59,7 +63,9 @@ export async function AddTeamMembershipToRequest(req: ReposAppRequest, res, next } else { const login = activeContext.getGitHubIdentity().username; try { - const statusResult = await team2.getMembershipEfficiently(login); + const statusResult = skipCache + ? await team2.getMembership(login, NoCacheNoBackground) + : await team2.getMembershipEfficiently(login); const value: IRequestTeamMembershipStatus = { membershipStatus: statusResult && (statusResult as ITeamMembershipRoleState).role @@ -94,7 +100,7 @@ export function getTeamPermissionsFromRequest(req: ReposAppRequest) { return req[teamPermissionsCacheKeyName] as IRequestTeamPermissions; } -export async function AddTeamPermissionsToRequest(req: ReposAppRequest, res, next) { +export async function AddTeamPermissionsToRequest(req: ReposAppRequest, res: Response, next: NextFunction) { if (req[teamPermissionsCacheKeyName]) { return next(); } diff --git a/middleware/healthCheck.ts b/middleware/healthCheck.ts index d8997c229..858057919 100644 --- a/middleware/healthCheck.ts +++ b/middleware/healthCheck.ts @@ -5,13 +5,15 @@ import Debug from 'debug'; import { IncomingHttpHeaders } from 'http'; +import { NextFunction, Response } from 'express'; + import type { ConfiguredHeaderProbe, ConfiguredGeneralProbe, ConfiguredProbeBase, } from '../config/webHealthProbes.types'; -import { ReposAppRequest, SiteConfiguration } from '../interfaces'; -import { CreateError } from '../transitional'; +import { IReposApplication, ReposAppRequest, SiteConfiguration } from '../interfaces'; +import { CreateError } from '../lib/transitional'; const dbg = Debug.debug('health'); @@ -29,7 +31,7 @@ enum ProbeType { } export default function initializeHealthCheck( - app, + app: IReposApplication, config: SiteConfiguration /* WebHealthProbeSubsetConfiguration */ ) { const { webHealthProbes: healthConfig } = config; @@ -84,8 +86,8 @@ export default function initializeHealthCheck( probeType: ProbeType, probeConfigs: ConfiguredHeaderProbe[], req: ReposAppRequest, - res, - next + res: Response, + next: NextFunction ) { for (const probeConfig of probeConfigs) { if (requestEligibleForCheck(checkType, probeType, probeConfig, req.headers)) { @@ -133,7 +135,12 @@ export default function initializeHealthCheck( return true; } - function returnExpressHealthCheck(checkType: HealthProbeType, req: ReposAppRequest, res, next) { + function returnExpressHealthCheck( + checkType: HealthProbeType, + req: ReposAppRequest, + res: Response, + next: NextFunction + ) { let result = null; try { result = checkHealth(checkType); @@ -149,7 +156,7 @@ export default function initializeHealthCheck( healthy: true, }; - if (enabledHeaderProbes.length > 0) { + if (enabledHeaderProbes.length > 0 && app) { dbg(`Configured header health probes: ${enabledHeaderProbes.length}`); app.get( '/health/readiness', @@ -160,7 +167,7 @@ export default function initializeHealthCheck( multipleHeaderHealthCheck.bind(null, HealthProbeType.Liveness, ProbeType.Header, enabledHeaderProbes) ); } - if (enabledGenericProbes.length > 0) { + if (enabledGenericProbes.length > 0 && app) { // General probes listen on their own type endpoint for (const genericProbeConfig of enabledGenericProbes) { const url = genericProbeConfig.endpoint || `/health/${genericProbeConfig.endpointSuffix}`; @@ -173,7 +180,7 @@ export default function initializeHealthCheck( ); } } - if (enabledGenericProbes.length + enabledGenericProbes.length > 0) { + if (app && enabledGenericProbes.length + enabledGenericProbes.length > 0) { dbg('Health probes listening'); // 404 on anything that was not handled by any active, allowed probe listeners app.use('/health/*', (req, res) => { diff --git a/middleware/index.ts b/middleware/index.ts index c45fdf0c3..32e2605c8 100644 --- a/middleware/index.ts +++ b/middleware/index.ts @@ -6,21 +6,23 @@ import bodyParser from 'body-parser'; import compression from 'compression'; import path from 'path'; +import { Express } from 'express'; +import passport from 'passport'; import Debug from 'debug'; const debug = Debug.debug('startup'); export * from './react'; -export * from './links'; +export * from './business/links'; export * from './business'; export * from './jsonError'; -import { hasStaticReactClientApp, stripDistFolderName } from '../transitional'; +import { hasStaticReactClientApp, stripDistFolderName } from '../lib/transitional'; import { StaticClientApp } from './staticClientApp'; import { StaticReactClientApp } from './staticClientApp2'; import { StaticSiteFavIcon, StaticSiteAssets } from './staticSiteAssets'; import connectSession from './session'; -import passportConfig from './passport-config'; +import passportConfig from './passportConfig'; import onboard from './onboarding'; import viewServices from '../lib/pugViewServices'; @@ -35,12 +37,14 @@ import routePassport from './passport-routes'; import routeApi from '../api'; -import { IProviders, IReposApplication, SiteConfiguration } from '../interfaces'; +import type { IProviders, IReposApplication, SiteConfiguration } from '../interfaces'; import { codespacesDevAssistant } from './codespaces'; +import { ExpressWithStatic } from './types'; export default async function initMiddleware( app: IReposApplication, - express, + express: Express, + providers: IProviders, config: SiteConfiguration, dirname: string, hasCustomRoutes: boolean, @@ -51,7 +55,6 @@ export default async function initMiddleware( config && config.typescript && config.typescript.appDirectory ? config.typescript.appDirectory : stripDistFolderName(dirname); - const providers = app.get('providers') as IProviders; const applicationProfile = providers.applicationProfile; if (initializationError) { providers.healthCheck.healthy = false; @@ -78,14 +81,15 @@ export default async function initMiddleware( if (applicationProfile.serveStaticAssets) { StaticSiteAssets(app, express); } + const expressWithStatic = express as ExpressWithStatic; if (hasStaticReactClientApp()) { - StaticReactClientApp(app, express, config); + StaticReactClientApp(app, expressWithStatic, config); } if (applicationProfile.serveClientAssets) { - StaticClientApp(app, express); + StaticClientApp(app, expressWithStatic); } providers.campaign = campaign(app); - let passport; + let passport: passport.PassportStatic; if (!initializationError) { if (config.containers && config.containers.deployment) { app.enable('trust proxy'); diff --git a/middleware/initialize.ts b/middleware/initialize.ts index b249d29bd..4422f6271 100644 --- a/middleware/initialize.ts +++ b/middleware/initialize.ts @@ -3,6 +3,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +import { Express, NextFunction, Response } from 'express'; import path from 'path'; import CosmosSessionStore from '../lib/cosmosSession'; @@ -14,11 +15,11 @@ import { createAndInitializeEntityMetadataProviderInstance, IEntityMetadataProvidersOptions, } from '../lib/entityMetadataProvider'; -import { createAndInitializeRepositoryMetadataProviderInstance } from '../entities/repositoryMetadata'; -import createAndInitializeOrganizationAnnotationProviderInstance from '../entities/organizationAnnotation'; +import { createAndInitializeRepositoryMetadataProviderInstance } from '../business/entities/repositoryMetadata'; +import createAndInitializeOrganizationAnnotationProviderInstance from '../business/entities/organizationAnnotation'; import { createMailAddressProviderInstance, IMailAddressProvider } from '../lib/mailAddressProvider'; -import ErrorRoutes from './error-routes'; +import ErrorRoutes from './errorRoutes'; import { createClient, RedisClientType } from 'redis'; import { Pool as PostgresPool } from 'pg'; @@ -26,6 +27,7 @@ import { Pool as PostgresPool } from 'pg'; import Debug from 'debug'; const debug = Debug.debug('startup'); const pgDebug = Debug.debug('pgpool'); +const nowDebug = Debug.debug('now'); import appInsights from './appInsights'; import keyVault from './keyVault'; @@ -36,9 +38,9 @@ import expressRoutes from '../routes/'; import alternateRoutes from './alternateApps'; import RedisHelper from '../lib/caching/redis'; -import { createTokenProvider } from '../entities/token'; -import { createAndInitializeApprovalProviderInstance } from '../entities/teamJoinApproval'; -import { CreateLocalExtensionKeyProvider } from '../entities/localExtensionKey'; +import { createTokenProvider } from '../business/entities/token'; +import { createAndInitializeApprovalProviderInstance } from '../business/entities/teamJoinApproval'; +import { CreateLocalExtensionKeyProvider } from '../business/entities/localExtensionKey'; import { CreateGraphProviderInstance, IGraphProvider } from '../lib/graphProvider/'; import initializeCorporateViews from './corporateViews'; @@ -46,16 +48,16 @@ import keyVaultResolver, { IKeyVaultSecretResolver } from '../lib/keyVaultResolv import { createMailProviderInstance } from '../lib/mailProvider/'; import { RestLibrary } from '../lib/github'; -import { CreateRepositoryCacheProviderInstance } from '../entities/repositoryCache'; -import { CreateRepositoryCollaboratorCacheProviderInstance } from '../entities/repositoryCollaboratorCache'; -import { CreateTeamCacheProviderInstance } from '../entities/teamCache'; -import { CreateTeamMemberCacheProviderInstance } from '../entities/teamMemberCache'; -import { CreateRepositoryTeamCacheProviderInstance } from '../entities/repositoryTeamCache'; -import { CreateOrganizationMemberCacheProviderInstance } from '../entities/organizationMemberCache'; +import { CreateRepositoryCacheProviderInstance } from '../business/entities/repositoryCache'; +import { CreateRepositoryCollaboratorCacheProviderInstance } from '../business/entities/repositoryCollaboratorCache'; +import { CreateTeamCacheProviderInstance } from '../business/entities/teamCache'; +import { CreateTeamMemberCacheProviderInstance } from '../business/entities/teamMemberCache'; +import { CreateRepositoryTeamCacheProviderInstance } from '../business/entities/repositoryTeamCache'; +import { CreateOrganizationMemberCacheProviderInstance } from '../business/entities/organizationMemberCache'; import QueryCache from '../business/queryCache'; -import { createAndInitializeOrganizationSettingProviderInstance } from '../entities/organizationSettings'; +import { createAndInitializeOrganizationSettingProviderInstance } from '../business/entities/organizationSettings'; import { IEntityMetadataProvider } from '../lib/entityMetadataProvider/entityMetadataProvider'; -import { createAndInitializeAuditLogRecordProviderInstance } from '../entities/auditLogRecord'; +import { createAndInitializeAuditLogRecordProviderInstance } from '../business/entities/auditLogRecord'; import CosmosCache from '../lib/caching/cosmosdb'; import BlobCache from '../lib/caching/blob'; import { StatefulCampaignProvider } from '../lib/campaigns'; @@ -63,7 +65,7 @@ import CosmosHelper from '../lib/cosmosHelper'; import { IQueueProcessor } from '../lib/queues'; import ServiceBusQueueProcessor from '../lib/queues/servicebus'; import AzureQueuesProcessor from '../lib/queues/azurequeue'; -import { UserSettingsProvider } from '../entities/userSettings'; +import { UserSettingsProvider } from '../business/entities/userSettings'; import getCompanySpecificDeployment from './companySpecificDeployment'; import routeCorrelationId from './correlationId'; @@ -71,13 +73,20 @@ import routeHsts from './hsts'; import routeSslify from './sslify'; import middlewareIndex from '.'; -import { ICacheHelper } from '../lib/caching'; -import { IApplicationProfile, IProviders, IReposApplication, SiteConfiguration } from '../interfaces'; -import initializeRepositoryProvider from '../entities/repository'; +import type { ICacheHelper } from '../lib/caching'; +import type { + ExecutionEnvironment, + ApplicationProfile, + IProviders, + IReposApplication, + SiteConfiguration, +} from '../interfaces'; +import initializeRepositoryProvider from '../business/entities/repository'; import { tryGetImmutableStorageProvider } from '../lib/immutable'; +import { GitHubAppPurposes } from '../lib/github/appPurposes'; -const DefaultApplicationProfile: IApplicationProfile = { - applicationName: 'GitHub Management Portal', +const DefaultApplicationProfile: ApplicationProfile = { + applicationName: 'Open Source Management Portal', serveStaticAssets: true, serveClientAssets: true, logDependencies: true, @@ -92,12 +101,13 @@ type CompanyStartupEntrypoint = ( ) => Promise; async function initializeAsync( - app: IReposApplication, - express, + executionEnvironment: ExecutionEnvironment, + providers: IProviders, + // app: IReposApplication, + // express, rootdir: string, config: SiteConfiguration ): Promise { - const providers = app.get('providers') as IProviders; providers.postgresPool = await ConnectPostgresPool(config.data.postgres); providers.linkProvider = await createAndInitializeLinkProviderInstance(providers, config); if (config.github.cache.provider === 'cosmosdb') { @@ -111,7 +121,6 @@ async function initializeAsync( } else if (config.github.cache.provider === 'redis') { const redisClient = await connectRedis(config, config.redis, 'cache'); const redisHelper = new RedisHelper({ redisClient, prefix: config.redis.prefix }); - // providers.redisClient = redisClient; providers.cacheProvider = redisHelper; } else { throw new Error('No cache provider available'); @@ -124,10 +133,7 @@ async function initializeAsync( } providers.graphProvider = await createGraphProvider(providers, config); - app.set('graphProvider', providers.graphProvider); - providers.mailAddressProvider = await createMailAddressProvider(config, providers); - app.set('mailAddressProvider', providers.mailAddressProvider); const mailProvider = createMailProviderInstance(config); if (mailProvider) { @@ -139,7 +145,6 @@ async function initializeAsync( } providers.github = configureGitHubLibrary(providers.cacheProvider, config); - app.set('github', providers.github); // always check if config exists to prevent crashing because of trying to access an undefined object const emOptions: IEntityMetadataProvidersOptions = { @@ -268,7 +273,7 @@ async function initializeAsync( providers.corporateAdministrationProfile = getCompanySpecificDeployment()?.administrationSection; providers.corporateViews = await initializeCorporateViews(providers, rootdir); - await dynamicStartup(config, providers, rootdir); + await dynamicStartup(executionEnvironment, config, providers, rootdir); const webhooksConfig = config.github.webhooks; if (webhooksConfig && webhooksConfig.provider) { @@ -290,16 +295,21 @@ async function initializeAsync( const repositoryMetadataProvider = await createAndInitializeRepositoryMetadataProviderInstance({ entityMetadataProvider: providerNameToInstance(config.entityProviders.repositorymetadata), }); - const operations = new Operations({ providers, repositoryMetadataProvider, github: providers.github }); + const operations = new Operations({ + executionEnvironment, + providers, + repositoryMetadataProvider, + github: providers.github, + }); await operations.initialize(); - app.set('operations', operations); providers.operations = operations; + GitHubAppPurposes.RegisterOperationsInstanceForBuiltInPurposes(operations); } catch (ignoredError2) { console.dir(ignoredError2); throw ignoredError2; } - await dynamicStartup(config, providers, rootdir, 'secondary'); + await dynamicStartup(executionEnvironment, config, providers, rootdir, 'secondary'); if (providers.applicationProfile.startup) { debug('Application provider-specific startup...'); @@ -307,17 +317,7 @@ async function initializeAsync( } } -function configureGitHubLibrary(cacheProvider: ICacheHelper, config): RestLibrary { - if ( - config && - config.github && - config.github.operations && - !config.github.operations.centralOperationsToken - ) { - debug( - 'WARNING: no central GitHub operations token is defined, some library operations may not succeed. ref: config.github.operations.centralOperationsToken var: GITHUB_CENTRAL_OPERATIONS_TOKEN' - ); - } +function configureGitHubLibrary(cacheProvider: ICacheHelper, config: SiteConfiguration): RestLibrary { const libraryContext = new RestLibrary({ config, cacheProvider, @@ -327,33 +327,44 @@ function configureGitHubLibrary(cacheProvider: ICacheHelper, config): RestLibrar // Asynchronous initialization for the Express app, configuration and data stores. export default async function initialize( + executionEnvironment: ExecutionEnvironment, app: IReposApplication, - express, + express: Express, rootdir: string, - config: any, + config: SiteConfiguration, exception: Error -): Promise { +): Promise { if (exception) { console.warn(`Startup exception: ${exception}`, exception?.stack); } if (!config || Object.getOwnPropertyNames(config).length === 0) { throw new Error('Empty configuration object'); } - if (!app.runtimeConfiguration) { + if (app && !app.runtimeConfiguration) { app.runtimeConfiguration = {}; } const applicationProfile = config?.web?.app && config.web.app !== 'repos' ? await alternateRoutes(config, app, config.web.app) : DefaultApplicationProfile; - const web = !(config?.skipModules && config.skipModules.has('web')); + const web = false === executionEnvironment.skipModules.has('web'); if (applicationProfile.webServer && !web) { applicationProfile.webServer = false; } - const containerPurpose = - config && config.isJobInternal ? 'job' : applicationProfile.webServer ? 'web application' : 'application'; - debug(`${containerPurpose} name: ${applicationProfile.applicationName}`); + const containerPurpose = executionEnvironment.isJob + ? 'job' + : applicationProfile.webServer + ? 'web application' + : 'application'; + if (executionEnvironment.entrypointName) { + debug(`${containerPurpose} name: ${executionEnvironment.entrypointName}`); + } + debug(`${containerPurpose} profile: ${applicationProfile.applicationName}`); debug(`environment: ${config?.debug?.environmentName || 'Unknown'}`); + if (config?.continuousDeployment) { + const values = Object.values(config.continuousDeployment).filter((x) => x); + values.length > 0 && debug(`build: ${values.join(', ')}`); + } const codespacesConfig = (config as SiteConfiguration)?.github?.codespaces; if (codespacesConfig?.connected === true && codespacesConfig.block === true) { @@ -377,54 +388,69 @@ export default async function initialize( exception = error; } } - app.set('started', new Date()); - app.config = config; + if (app) { + app.set('started', executionEnvironment.started); + app.config = config; + } if (exception) { // Once app insights is available, will try to log this exception; display for now. console.dir(exception); } - app.set('basedir', rootdir); + if (app) { + app.set('basedir', rootdir); + } const providers: IProviders = { app, basedir: rootdir, applicationProfile, }; - app.set('providers', providers); - app.providers = providers; - app.set('runtimeConfig', config); + executionEnvironment.providers = providers; + if (app) { + app.set('providers', providers); + app.providers = providers; + app.set('runtimeConfig', config); + } providers.healthCheck = healthCheck(app, config); if (applicationProfile.webServer) { - if (!app.startServer) { + if (!app) { + throw new Error('app (Express) is required for web applications'); + } else if (!app.startServer) { throw new Error(`app.startServer is required for web applications`); } await app.startServer(); } - app.use(routeCorrelationId); - providers.insights = appInsights(app, config); - app.set('appInsightsClient', providers.insights); + if (app) { + app.use(routeCorrelationId); + } + const insights = appInsights(providers, executionEnvironment, app, config); + providers.insights = insights; if (!exception && (!config || !config.activeDirectory)) { exception = new Error( `config.activeDirectory.clientId and config.activeDirectory.clientSecret are required to initialize KeyVault` ); } - app.use('*', (req, res, next) => { - if (providers.healthCheck.ready) { - return next(); - } - return res.send('Service not ready.'); - }); + if (app) { + app.use('*', (req, res: Response, next: NextFunction) => { + if (providers.healthCheck.ready) { + return next(); + } + return res.send('Service not ready.'); + }); + } // See docs/configuration.md for all this - if (config?.containers?.deployment) { - debug('Container deployment: HTTP: listening, HSTS: on'); - app.use(routeHsts); - } else if (config?.containers?.docker) { - debug('Docker image: HTTP: listening, HSTS: off'); - } else if (config.webServer.allowHttp) { - debug('development mode: HTTP: listening, HSTS: off'); - } else { - debug('non-container production mode: HTTP: redirect to HTTPS, HSTS: on'); - const sslifyRouter = routeSslify(config.webServer); - sslifyRouter && app.use(sslifyRouter); + if (app) { + if (config?.containers?.deployment) { + debug('Container deployment: HTTP: listening, HSTS: on'); + app.use(routeHsts); + } else if (config?.containers?.docker) { + debug('Docker image: HTTP: listening, HSTS: off'); + } else if (config.webServer.allowHttp) { + debug('development mode: HTTP: listening, HSTS: off'); + } else { + debug('non-container production mode: HTTP: redirect to HTTPS, HSTS: on'); + const sslifyRouter = routeSslify(config.webServer); + sslifyRouter && app.use(sslifyRouter); + } } if (!exception) { const kvConfig = { @@ -437,7 +463,7 @@ export default async function initialize( try { const keyVaultClient = keyVault(kvConfig); keyEncryptionKeyResolver = keyVaultResolver(keyVaultClient); - app.set('keyEncryptionKeyResolver', keyEncryptionKeyResolver); + app && app.set('keyEncryptionKeyResolver', keyEncryptionKeyResolver); providers.keyEncryptionKeyResolver = keyEncryptionKeyResolver; debug('configuration secrets resolved'); } catch (noKeyVault) { @@ -449,7 +475,7 @@ export default async function initialize( } } try { - await initializeAsync(app, express, rootdir, config); + await initializeAsync(executionEnvironment, providers, rootdir, config); } catch (initializeError) { console.dir(initializeError); debug(`Initialization failure: ${initializeError}`); @@ -458,7 +484,9 @@ export default async function initialize( } const hasCustomRoutes = !!applicationProfile.customRoutes; try { - await middlewareIndex(app, express, config, rootdir, hasCustomRoutes, exception); + if (app) { + await middlewareIndex(app, express, providers, config, rootdir, hasCustomRoutes, exception); + } } catch (middlewareError) { exception = middlewareError; } @@ -466,13 +494,12 @@ export default async function initialize( if (!exception) { if (hasCustomRoutes) { await applicationProfile.customRoutes(); - } else { + } else if (app) { app.use('/', expressRoutes); } } else { console.error(exception); - const appInsightsClient = providers.insights; - const crash = (error) => { + const crash = (error: Error) => { return () => { debug('App crashed because of an initialization error.'); console.log(error.message); @@ -482,15 +509,15 @@ export default async function initialize( process.exit(1); }; }; - if (appInsightsClient) { - appInsightsClient.trackException({ + if (insights) { + insights.trackException({ exception, properties: { info: 'App crashed while initializing', }, }); try { - appInsightsClient.flush({ isAppCrashing: true, callback: crash(exception) }); + insights.flush({ isAppCrashing: true, callback: crash(exception) }); } catch (sendError) { console.dir(sendError); crash(exception)(); @@ -500,10 +527,24 @@ export default async function initialize( } } await ErrorRoutes(app, exception); - return app; + if (config?.debug?.breakConsoleEveryMinute === true) { + const isNowDebugging = Debug.enabled('now'); + const everyMinute = () => { + const display = new Date().toISOString().substring(0, 19).replace('T', ' '); + if (isNowDebugging) { + nowDebug(display); + } else { + console.log(); + console.log(display); + } + }; + everyMinute(); + setInterval(everyMinute, 60000); + } + return executionEnvironment; } -function createGraphProvider(providers: IProviders, config: any): Promise { +function createGraphProvider(providers: IProviders, config: SiteConfiguration): Promise { return new Promise((resolve, reject) => { // The graph provider is optional. A graph provider can connect to a // corporate directory to validate or lookup employees and other @@ -601,7 +642,10 @@ async function connectRedis( return redisClient; } -async function createMailAddressProvider(config: any, providers: IProviders): Promise { +async function createMailAddressProvider( + config: SiteConfiguration, + providers: IProviders +): Promise { const options = { config: config, providers: providers, @@ -609,7 +653,13 @@ async function createMailAddressProvider(config: any, providers: IProviders): Pr return createMailAddressProviderInstance(options); } -async function dynamicStartup(config: any, providers: IProviders, rootdir: string, stage?: string) { +async function dynamicStartup( + executionEnvironment: ExecutionEnvironment, + config: SiteConfiguration, + providers: IProviders, + rootdir: string, + stage?: string +) { const p = config?.startup?.path; if (p) { try { @@ -625,6 +675,7 @@ async function dynamicStartup(config: any, providers: IProviders, rootdir: strin } const promise = (entrypoint as CompanyStartupEntrypoint).call( null, + executionEnvironment, config, providers, rootdir @@ -632,6 +683,9 @@ async function dynamicStartup(config: any, providers: IProviders, rootdir: strin await promise; debug(`company-specific ${stage || 'startup'} complete (${p})`); } catch (dynamicLoadError) { + if (dynamicLoadError.stack) { + console.error(dynamicLoadError.stack); + } throw new Error(`config.startup.path=${p} could not successfully load: ${dynamicLoadError}`); } } diff --git a/middleware/jsonError.ts b/middleware/jsonError.ts index 48cdd1bc9..a8f40058d 100644 --- a/middleware/jsonError.ts +++ b/middleware/jsonError.ts @@ -3,17 +3,22 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +import { NextFunction, Response } from 'express'; + +import { ReposAppRequest } from '../interfaces'; + interface IErrorJson extends Error { json?: boolean; statusCode?: string | number; } -export function json404(req, res, next) { +export function json404(req: ReposAppRequest, res: Response, next: NextFunction) { return next(jsonError('Endpoint not found', 404)); } -export function isJsonError(error: IErrorJson | string | Error) { - return error && error['json'] === true; +export function isJsonError(error: IErrorJson | string | Error, url: string) { + const errorAsAny = error as any; + return errorAsAny?.json === true || url?.startsWith('/api/'); } export function jsonError(error: IErrorJson | string | Error, statusCode?: number): IErrorJson { diff --git a/middleware/keyVault.ts b/middleware/keyVault.ts index f03629d5d..77c90a60b 100644 --- a/middleware/keyVault.ts +++ b/middleware/keyVault.ts @@ -4,7 +4,7 @@ // import { ClientSecretCredential } from '@azure/identity'; -import { KeyVaultSecret, SecretClient } from '@azure/keyvault-secrets'; +import { SecretClient } from '@azure/keyvault-secrets'; export interface IKeyVaultConfigurationOptions { tenantId: string; diff --git a/middleware/locals.ts b/middleware/locals.ts index 2fbe3c770..59ed4772f 100644 --- a/middleware/locals.ts +++ b/middleware/locals.ts @@ -4,11 +4,12 @@ // import os from 'os'; -import { ReposAppRequest } from '../interfaces'; +import { NextFunction, Response } from 'express'; -import { getProviders } from '../transitional'; +import { ReposAppRequest } from '../interfaces'; +import { getProviders } from '../lib/transitional'; -export default function (req: ReposAppRequest, res, next) { +export default function (req: ReposAppRequest, res: Response, next: NextFunction) { const { config, viewServices } = getProviders(req); req.app.locals.correlationId = req.correlationId; req.app.locals.scrubbedUrl = req.scrubbedUrl; diff --git a/middleware/logger.ts b/middleware/logger.ts index ef63838d3..7a3689a08 100644 --- a/middleware/logger.ts +++ b/middleware/logger.ts @@ -6,7 +6,7 @@ import logger from 'morgan'; import { ReposAppRequest } from '../interfaces'; -import { getProviders } from '../transitional'; +import { getProviders } from '../lib/transitional'; const encryptionMetadataKey = '_ClientEncryptionMetadata2'; const piiFormat = diff --git a/middleware/lowercaser.ts b/middleware/lowercaser.ts index 7248e5427..d4eecec20 100644 --- a/middleware/lowercaser.ts +++ b/middleware/lowercaser.ts @@ -3,8 +3,12 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +import { NextFunction, Response } from 'express'; + +import { ReposAppRequest } from '../interfaces'; + export default function (params) { - return function (req, res, next) { + return function (req: ReposAppRequest, res: Response, next: NextFunction) { // lowercase parameters Object.getOwnPropertyNames(req.params).forEach((param) => { if (params.indexOf(param) > -1) { diff --git a/middleware/officeHyperlinks.ts b/middleware/officeHyperlinks.ts index e3e83bd96..5a0d6daf3 100644 --- a/middleware/officeHyperlinks.ts +++ b/middleware/officeHyperlinks.ts @@ -5,7 +5,8 @@ // I no longer believe this file FYI... I don't think we need this any longer. -import { getProviders } from '../transitional'; +import { NextFunction, Response } from 'express'; +import { getProviders } from '../lib/transitional'; // Office uses a specialized pre-fetch to learn more about hyperlinks before // opening. As a result, if the Office user agent is in use, and the @@ -17,7 +18,7 @@ import { getProviders } from '../transitional'; const GenericOfficeUserAgent = 'ms-office'; const WordUserAgent = 'Microsoft Office Word'; -export default function supportOfficeHyperlinks(req, res, next) { +export default function supportOfficeHyperlinks(req, res: Response, next: NextFunction) { const { insights } = getProviders(req); const userAgent = req.headers['user-agent']; const isAuthenticated = req.isAuthenticated ? req.isAuthenticated() : false; diff --git a/middleware/onboarding.ts b/middleware/onboarding.ts index 225bbbe2b..6a6b34535 100644 --- a/middleware/onboarding.ts +++ b/middleware/onboarding.ts @@ -3,7 +3,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { IProviders } from '../interfaces'; +import { IProviders, IReposApplication, SiteConfiguration } from '../interfaces'; // ---------------------------------------------------------------------------- // Onboarding helper @@ -14,7 +14,7 @@ import { IProviders } from '../interfaces'; // rather a configuration/app initialization method just stored here to keep it // out of the way. // ---------------------------------------------------------------------------- -export default async function Onboard(app, config) { +export default async function Onboard(app: IReposApplication, config: SiteConfiguration) { const { operations } = app.settings.providers as IProviders; const onboardingOrganizations = config.github.organizations?.onboarding || []; for (const orgEntry of onboardingOrganizations) { diff --git a/middleware/passport-routes.ts b/middleware/passport-routes.ts index 642e332cc..a0bb18142 100644 --- a/middleware/passport-routes.ts +++ b/middleware/passport-routes.ts @@ -3,11 +3,11 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Response, NextFunction } from 'express'; +import type { Response, NextFunction } from 'express'; -import { redirectToReferrer, storeReferrer } from '../utils'; -import { getProviders } from '../transitional'; -import { ReposAppRequest, IAppSession } from '../interfaces'; +import { redirectToReferrer, storeReferrer } from '../lib/utils'; +import { getProviders } from '../lib/transitional'; +import type { ReposAppRequest, IAppSession } from '../interfaces'; import getCompanySpecificDeployment from './companySpecificDeployment'; import { attachAadPassportRoutes } from './passport/aadRoutes'; import { attachGitHubPassportRoutes } from './passport/githubRoutes'; @@ -228,6 +228,7 @@ export default function configurePassport(app, passport, config) { ? `Your ${config.brand.companyName} and GitHub accounts have been unlinked. You no longer have access to any ${config.brand.companyName} organizations, and you have been signed out of this portal.` : 'Goodbye', title: 'Goodbye', + clearLocalStorage: true, buttonText: unlinked ? 'Sign in to connect a new account' : 'Sign in', config: config.obfuscatedConfig, }); diff --git a/middleware/passport/aadRoutes.ts b/middleware/passport/aadRoutes.ts index 59780a355..10fa64010 100644 --- a/middleware/passport/aadRoutes.ts +++ b/middleware/passport/aadRoutes.ts @@ -5,22 +5,22 @@ import { NextFunction, Response } from 'express'; import { PassportStatic } from 'passport'; -import { IReposError, ReposAppRequest } from '../../interfaces'; -import { getProviders } from '../../transitional'; -import { isCodespacesAuthenticating } from '../../utils'; +import { type IReposApplication, type IReposError, ReposAppRequest } from '../../interfaces'; +import { getProviders } from '../../lib/transitional'; +import { isCodespacesAuthenticating } from '../../lib/utils'; import { IPrimaryAuthenticationHelperMethods } from '../passport-routes'; import { aadStrategyUserPropertyName } from './aadStrategy'; const aadPassportStrategyName = 'azure-active-directory'; export function attachAadPassportRoutes( - app, + app: IReposApplication, config: any, passport: PassportStatic, helpers: IPrimaryAuthenticationHelperMethods ) { const signinPath = isCodespacesAuthenticating(config, 'aad') ? 'sign-in' : 'signin'; - app.get(`/${signinPath}`, function (req: ReposAppRequest, res, next) { + app.get(`/${signinPath}`, function (req: ReposAppRequest, res: Response, next: NextFunction) { if (req.isAuthenticated()) { const username = req.user?.azure?.username; if (username) { @@ -81,7 +81,7 @@ export function attachAadPassportRoutes( // links from apps that temporarily prevent sessions. Technically this seems to // impact Windows users who use Word to open links to the site. Collecting // telemetry for now. - app.get('/auth/azure/callback', (req: ReposAppRequest, res, next) => { + app.get('/auth/azure/callback', (req: ReposAppRequest, res: Response, next: NextFunction) => { const { insights } = getProviders(req); const isAuthenticated = req.isAuthenticated(); insights?.trackEvent({ diff --git a/middleware/passport/aadStrategy.ts b/middleware/passport/aadStrategy.ts index 25e894238..9d1db1af8 100644 --- a/middleware/passport/aadStrategy.ts +++ b/middleware/passport/aadStrategy.ts @@ -9,10 +9,10 @@ const debug = Debug.debug('startup'); import { AuthorizationCode } from 'simple-oauth2'; import { OIDCStrategy } from 'passport-azure-ad'; -import { IProviders } from '../../interfaces'; +import type { IProviders, IReposApplication, SiteConfiguration } from '../../interfaces'; import { GraphUserType } from '../../lib/graphProvider'; -import { getCodespacesHostname, isCodespacesAuthenticating } from '../../utils'; +import { getCodespacesHostname, isCodespacesAuthenticating } from '../../lib/utils'; export const aadStrategyName = 'azure-active-directory'; export const aadStrategyUserPropertyName = 'azure'; @@ -25,19 +25,64 @@ interface IAADUser { displayName: string; oid: string; username: string; - oauthToken?: string; + // oauthToken?: string; // we aren't using this, no need to store these } +/* cSpell:disable */ +type AadJwtJson = { + aio: string; + amr: string; + family_name: string; + given_name: string; + ipaddr: string; + name: string; + oid: string; + onprem_sid: string; + rh: string; + sub: string; + tid: string; + unique_name: string; + upn: string; + uti: string; + ver: string; +}; +/* cSpell:enable */ + +type AadResponseProfile = { + _json: AadJwtJson; + _raw: string; + displayName: string; + emails: undefined; + name: { + familyName: string; + givenName: string; + middleName: string; + }; + oid: string; + sub: string; + upn: string; +}; + +type AadBearerToken = { + access_token: string; + expireS_in: string; + expires_on: string; + ext_expires_in: string; + id_token: string; + refresh_token: string; + token_type: 'Bearer'; +}; + async function login( - app, - config, + app: IReposApplication, + config: SiteConfiguration, client: AuthorizationCode, - iss, - sub, - profile, + iss: string, + sub: string, + profile: AadResponseProfile, accessToken: string, refreshToken: string, - params + params: AadBearerToken ): Promise { const { graphProvider, insights } = app.settings.providers as IProviders; const oauthToken = JSON.stringify(params); @@ -55,7 +100,7 @@ async function login( displayName: impersonationResult.displayName, oid: impersonationResult.id, username: impersonationResult.userPrincipalName, - oauthToken, + // oauthToken, }, }; } @@ -75,21 +120,21 @@ async function login( displayName: profile.displayName, oid: profile.oid, username: profile.upn, - oauthToken, + // oauthToken, }, }; } function activeDirectorySubset( - app, - config, + app: IReposApplication, + config: SiteConfiguration, client: AuthorizationCode, - iss, - sub, - profile, + iss: string, + sub: string, + profile: AadResponseProfile, accessToken: string, refreshToken: string, - params, + params: AadBearerToken, done ) { login(app, config, client, iss, sub, profile, accessToken, refreshToken, params) @@ -101,9 +146,9 @@ function activeDirectorySubset( }); } -export default function createAADStrategy(app, config) { +export default function createAADStrategy(app: IReposApplication, config: SiteConfiguration) { const { redirectUrl, tenantId, clientId, clientSecret } = config.activeDirectory; - const codespaces = config?.github?.codespaces || {}; + const codespaces = config?.github?.codespaces; if (!clientId) { debug('No Azure Active Directory clientID configured, corporate authentication will be unavailable.'); return {}; diff --git a/middleware/passport/encryptionSerializer.ts b/middleware/passport/encryptionSerializer.ts index b87cff659..c35cfb106 100644 --- a/middleware/passport/encryptionSerializer.ts +++ b/middleware/passport/encryptionSerializer.ts @@ -4,7 +4,7 @@ // import { decryptEntityAsync, encryptEntityAsync, IEncryptionOptions } from '../../lib/encryption'; -import { wrapError } from '../../utils'; +import { wrapError } from '../../lib/utils'; import { LegacySerializer } from './serializer'; // NOTE TO DEVELOPERS: we are no longer using encrypted sessions at our company diff --git a/middleware/passport/githubRoutes.ts b/middleware/passport/githubRoutes.ts index 07e0a287c..03f441ca7 100644 --- a/middleware/passport/githubRoutes.ts +++ b/middleware/passport/githubRoutes.ts @@ -5,10 +5,11 @@ import { NextFunction, Response } from 'express'; import querystring from 'querystring'; +import { PassportStatic } from 'passport'; -import { ReposAppRequest } from '../../interfaces'; -import { getProviders } from '../../transitional'; -import { isCodespacesAuthenticating } from '../../utils'; +import { IReposApplication, ReposAppRequest, SiteConfiguration } from '../../interfaces'; +import { getProviders } from '../../lib/transitional'; +import { isCodespacesAuthenticating } from '../../lib/utils'; import { IAuthenticationHelperMethods } from '../passport-routes'; import { getGithubAppConfigurationOptions, @@ -19,9 +20,9 @@ import { } from '../passport/githubStrategy'; export function attachGitHubPassportRoutes( - app, - config: any, - passport, + app: IReposApplication, + config: SiteConfiguration, + passport: PassportStatic, helpers: IAuthenticationHelperMethods ) { const signinPath = isCodespacesAuthenticating(config, 'github') ? 'sign-in' : 'signin'; diff --git a/middleware/passport/githubStrategy.ts b/middleware/passport/githubStrategy.ts index ff98687dd..fd056d788 100644 --- a/middleware/passport/githubStrategy.ts +++ b/middleware/passport/githubStrategy.ts @@ -5,10 +5,21 @@ import { Strategy as GithubStrategy } from 'passport-github'; -import { IGitHubAccountDetails, IProviders } from '../../interfaces'; -import { getCodespacesHostname, isCodespacesAuthenticating, isEnterpriseManagedUserLogin } from '../../utils'; +import { + getCodespacesHostname, + isCodespacesAuthenticating, + isEnterpriseManagedUserLogin, +} from '../../lib/utils'; +import type { + IGitHubAccountDetails, + IProviders, + IReposApplication, + SiteConfiguration, +} from '../../interfaces'; +import type { ConfigGitHubCodespaces } from '../../config/github.codespaces.types'; import Debug from 'debug'; +import { ConfigGitHubOAuth2 } from '../../config/github.oauth2.types'; const debug = Debug.debug('startup'); export const githubStrategyName = 'github'; @@ -55,7 +66,7 @@ function impersonatedIdentityFromDetails( } function githubResponseToSubset( - app, + app: IReposApplication, modernAppInUse: boolean, accessToken: string, refreshToken: string, @@ -73,7 +84,7 @@ function githubResponseToSubset( } async function githubResponseToSubsetEx( - app, + app: IReposApplication, modernAppInUse: boolean, accessToken: string, refreshToken: string, @@ -142,7 +153,7 @@ function githubResponseToIncreasedScopeSubset( return done(null, subset); } -export function getGithubAppConfigurationOptions(config) { +export function getGithubAppConfigurationOptions(config: SiteConfiguration) { let legacyOAuthApp = config?.github?.oauth2?.clientId && config?.github?.oauth2?.clientSecret ? config.github.oauth2 : null; const customerFacingApp = @@ -168,9 +179,9 @@ export function getGithubAppConfigurationOptions(config) { }; } -export default function createGithubStrategy(app, config) { +export default function createGithubStrategy(app: IReposApplication, config: SiteConfiguration) { const strategies = {}; - const codespaces = config?.github?.codespaces || {}; + const codespaces = config?.github?.codespaces || ({} as ConfigGitHubCodespaces); const { modernAppInUse, githubAppConfiguration, useIncreasedScopeLegacyAppIfNeeded } = getGithubAppConfigurationOptions(config); if (!githubAppConfiguration?.clientId) { @@ -183,7 +194,7 @@ export default function createGithubStrategy(app, config) { const finalCallbackUrl = isCodespacesAuthenticating(config, 'github') && !codespaces?.block ? getCodespacesHostname(config) + redirectSuffix - : githubAppConfiguration.callbackUrl; + : (githubAppConfiguration as ConfigGitHubOAuth2)?.callbackUrl; let clientId = githubAppConfiguration.clientId; let clientSecret = githubAppConfiguration.clientSecret; let codespacesOverrideText = ''; diff --git a/middleware/passport-config.ts b/middleware/passportConfig.ts similarity index 96% rename from middleware/passport-config.ts rename to middleware/passportConfig.ts index 064eb605e..3f52f94a8 100644 --- a/middleware/passport-config.ts +++ b/middleware/passportConfig.ts @@ -3,7 +3,9 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +import { NextFunction, Response } from 'express'; import passport from 'passport'; + import { IKeyVaultSecretResolver } from '../lib/keyVaultResolver'; import getCompanySpecificDeployment from './companySpecificDeployment'; @@ -73,7 +75,7 @@ export default function (app: IReposApplication, config: SiteConfiguration) { passport.deserializeUser(serializer.deserialize(serializerOptions)); serializer.initialize(serializerOptions, app); - app.use((req: ReposAppRequest, res, next) => { + app.use((req: ReposAppRequest, res: Response, next: NextFunction) => { if (req?.insights?.commonProperties && config.authentication.scheme === 'aad' && req?.user?.azure?.oid) { req.insights.commonProperties.aadId = req.user.azure.oid; } diff --git a/middleware/rawBodyParser.ts b/middleware/rawBodyParser.ts index 984f9852e..1ef2e395a 100644 --- a/middleware/rawBodyParser.ts +++ b/middleware/rawBodyParser.ts @@ -3,10 +3,11 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +import { NextFunction, Response } from 'express'; import { ReposAppRequest } from '../interfaces'; -import { isWebhookIngestionEndpointEnabled } from '../transitional'; +import { isWebhookIngestionEndpointEnabled } from '../lib/transitional'; -export default function rawBodyParser(req: ReposAppRequest, res, next) { +export default function rawBodyParser(req: ReposAppRequest, res: Response, next: NextFunction) { if (!isWebhookIngestionEndpointEnabled(req)) { return next(); } diff --git a/middleware/react.ts b/middleware/react.ts index b797e1d76..8738e76fd 100644 --- a/middleware/react.ts +++ b/middleware/react.ts @@ -3,15 +3,15 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Response } from 'express'; +import { NextFunction, Response } from 'express'; import fs from 'fs'; import path from 'path'; import appPackage from '../package.json'; import { getStaticBlobCacheFallback } from '../lib/staticBlobCacheFallback'; -import { getProviders, splitSemiColonCommas } from '../transitional'; -import { ReposAppRequest } from '../interfaces'; +import { getProviders, splitSemiColonCommas } from '../lib/transitional'; +import type { ReposAppRequest, SiteConfiguration } from '../interfaces'; import { IndividualContext } from '../business/user'; const staticReactPackageNameKey = 'static-react-package-name'; @@ -31,37 +31,44 @@ type PackageJsonSubset = { flights?: Record; }; +type BasicFlightingOptions = { + enabled: boolean; +}; + type ContentOptions = { html: string; package: PackageJsonSubset; }; -type FlightingOptions = ContentOptions & { - enabled: boolean; - divertEveryone: boolean; - staticFlightIds?: Set; - flightName: string; -}; +type FlightingOptions = BasicFlightingOptions & + ContentOptions & { + divertEveryone?: boolean; + staticFlightIds?: Set; + flightName?: string; + }; export function injectReactClient() { const standardContent = getReactScriptsIndex(staticClientPackageName); + let flightingBasics: BasicFlightingOptions = null; let flightingOptions: FlightingOptions = null; - return function injectedRoute(req: ReposAppRequest, res, next) { + return function injectedRoute(req: ReposAppRequest, res: Response, next: NextFunction) { const { config } = getProviders(req); // special passthrough if (req.path.includes('/byClient')) { return next(); } if (!flightingOptions) { - flightingOptions = evaluateFlightConditions(req); + flightingBasics = evaluateFlightConditions(req); + flightingOptions = flightingBasics as FlightingOptions; } const activeContext = (req.individualContext || req.apiContext) as IndividualContext; - const flightAvailable = flightingOptions.enabled && flightingOptions.html; - const flightName = flightAvailable ? flightingOptions.flightName : null; + const flightEnabled = flightingBasics?.enabled === true; + const flightAvailable = flightEnabled && flightingOptions?.html; + const flightName = flightingOptions?.flightName; const userFlighted = - flightingOptions.divertEveryone === true || + flightingOptions?.divertEveryone === true || (activeContext?.corporateIdentity?.id && - flightingOptions.staticFlightIds?.has(activeContext.corporateIdentity.id)); + flightingOptions?.staticFlightIds?.has(activeContext.corporateIdentity.id)); const userFlightOverride = req.query.flight === '0' || req.query.flight === '1' ? req.query.flight : undefined; let inFlight = flightAvailable && (userFlighted || req.query.flight === '1'); @@ -141,7 +148,7 @@ export function injectReactClient() { }; } -function evaluateFlightConditions(req: ReposAppRequest): FlightingOptions { +function evaluateFlightConditions(req: ReposAppRequest): FlightingOptions | BasicFlightingOptions { const { config } = getProviders(req); if (config?.client?.flighting?.enabled === true && staticClientFlightingPackageName) { const options = getReactScriptsIndex(staticClientFlightingPackageName) as FlightingOptions; @@ -157,9 +164,12 @@ function evaluateFlightConditions(req: ReposAppRequest): FlightingOptions { ); return options; } + return { + enabled: false, + }; } -function getUserClientFeatureFlags(config: any, corporateId: string) { +function getUserClientFeatureFlags(config: SiteConfiguration, corporateId: string) { const featureFlagList = config?.client?.flighting?.featureFlagUsers; if (featureFlagList && typeof featureFlagList === 'object') { const flights = []; diff --git a/middleware/scrubbedUrl.ts b/middleware/scrubbedUrl.ts index 0ed6f9496..1600724a8 100644 --- a/middleware/scrubbedUrl.ts +++ b/middleware/scrubbedUrl.ts @@ -3,9 +3,12 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +import { NextFunction, Response } from 'express'; +import { ReposAppRequest } from '../interfaces'; + // Scrub the incoming URL value(s) in the request, replacing tokens and other // secrets. -export default function (req, res, next) { +export default function (req: ReposAppRequest, res: Response, next: NextFunction) { let url = req.originalUrl || req.url; const secretKeys = ['code', 'token']; for (let i = 0; i < secretKeys.length; i++) { @@ -16,5 +19,5 @@ export default function (req, res, next) { } } req.scrubbedUrl = url; - next(); + return next(); } diff --git a/middleware/sslify.ts b/middleware/sslify.ts index 4fd121d2d..2b7a83dc5 100644 --- a/middleware/sslify.ts +++ b/middleware/sslify.ts @@ -4,6 +4,7 @@ // import sslify from 'express-sslify'; + import type { ConfigWebServer } from '../config/webServer.types'; export default function (webServerConfig: ConfigWebServer) { diff --git a/middleware/staticClientApp.ts b/middleware/staticClientApp.ts index 14af5da60..2cebc2dbe 100644 --- a/middleware/staticClientApp.ts +++ b/middleware/staticClientApp.ts @@ -8,10 +8,13 @@ import appPackage from '../package.json'; const packageVariableName = 'static-client-package-name'; const otherPackageVariableName = 'static-react-package-name'; +import type { IReposApplication } from '../interfaces'; +import type { ExpressWithStatic } from './types'; + import Debug from 'debug'; const debug = Debug.debug('startup'); -export function StaticClientApp(app, express) { +export function StaticClientApp(app: IReposApplication, express: ExpressWithStatic) { // Serve/host the static client app from the location reported by the private // NPM module for the Ember app. Assumes that the inclusion of the package // returns the path to host. diff --git a/middleware/staticClientApp2.ts b/middleware/staticClientApp2.ts index 7fdc795bd..73112124d 100644 --- a/middleware/staticClientApp2.ts +++ b/middleware/staticClientApp2.ts @@ -3,7 +3,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { hasStaticReactClientApp } from '../transitional'; +import { hasStaticReactClientApp } from '../lib/transitional'; import appPackage from '../package.json'; @@ -16,6 +16,7 @@ const staticReactFlightingPackageNameKey = 'static-react-flight-package-name'; const staticClientFlightingPackageName = appPackage[staticReactFlightingPackageNameKey]; import Debug from 'debug'; +import { ExpressWithStatic } from './types'; const debug = Debug.debug('startup'); export type RuntimeConfigurationClient = { @@ -31,7 +32,11 @@ export type RootRuntimeConfigurationClient = { client?: RuntimeConfigurationClient; }; -export function StaticReactClientApp(app: IReposApplication, express, config: SiteConfiguration) { +export function StaticReactClientApp( + app: IReposApplication, + express: ExpressWithStatic, + config: SiteConfiguration +) { const clientRuntimeConfiguration: RuntimeConfigurationClient = {}; app.runtimeConfiguration.client = clientRuntimeConfiguration; diff --git a/middleware/staticSiteAssets.ts b/middleware/staticSiteAssets.ts index 155e0c41d..5d56d4e50 100644 --- a/middleware/staticSiteAssets.ts +++ b/middleware/staticSiteAssets.ts @@ -12,7 +12,7 @@ const debug = Debug.debug('startup'); import favicon from 'serve-favicon'; import path from 'path'; -import { CreateError } from '../transitional'; +import { CreateError } from '../lib/transitional'; const appRootPath = appRoot.toString(); diff --git a/middleware/supportMultipleAuthProviders.ts b/middleware/supportMultipleAuthProviders.ts index 4cdb353a8..0905cbab0 100644 --- a/middleware/supportMultipleAuthProviders.ts +++ b/middleware/supportMultipleAuthProviders.ts @@ -3,9 +3,11 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +import { NextFunction, Response } from 'express'; + import { jsonError } from './jsonError'; import { IApiRequest } from './apiReposAuth'; -import { getProviders } from '../transitional'; +import { getProviders } from '../lib/transitional'; // We have made a decision to not use Passport for the API routes, which is why this // performs some passport-like functionality... @@ -24,7 +26,7 @@ export default function returnCombinedMiddleware(supportedProviders) { if (totalProviders <= 0) { throw new Error('supportedProviders must provide at least one provider to use for auth'); } - return function middleware(req: IApiRequest, res, next) { + return function middleware(req: IApiRequest, res: Response, next: NextFunction) { const { insights } = getProviders(req); let i = 0; let currentProvider = supportedProviders[i]; diff --git a/scripts/migrateLinks/index.ts b/middleware/types.ts similarity index 50% rename from scripts/migrateLinks/index.ts rename to middleware/types.ts index 6c6856b80..50b8e1de7 100644 --- a/scripts/migrateLinks/index.ts +++ b/middleware/types.ts @@ -3,9 +3,8 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import Job from './task'; -import app from '../../app'; +import { Express } from 'express'; -app.runJob(Job, { - defaultDebugOutput: 'cache,restapi', -}); +export type ExpressWithStatic = Express & { + static: (path: string, options?: any) => Express; +}; diff --git a/package-lock.json b/package-lock.json index 911fbbe64..5e48390e4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,7 +1,7 @@ { "name": "opensource-management-portal", "version": "7.1.0", - "lockfileVersion": 2, + "lockfileVersion": 3, "requires": true, "packages": { "": { @@ -9,21 +9,22 @@ "version": "7.1.0", "license": "MIT", "dependencies": { - "@azure/cosmos": "3.17.3", + "@azure/cosmos": "4.0.0", "@azure/data-tables": "13.2.2", - "@azure/identity": "3.2.2", + "@azure/identity": "4.0.0", "@azure/keyvault-secrets": "4.7.0", - "@azure/service-bus": "7.9.0", - "@azure/storage-blob": "12.14.0", - "@azure/storage-queue": "12.14.0", - "@octokit/auth-app": "4.0.13", - "@octokit/plugin-paginate-graphql": "2.0.1", - "@octokit/request": "6.2.5", - "@octokit/rest": "19.0.11", + "@azure/service-bus": "7.9.3", + "@azure/storage-blob": "12.17.0", + "@azure/storage-queue": "12.16.0", + "@octokit/auth-app": "6.0.3", + "@octokit/plugin-paginate-graphql": "4.0.0", + "@octokit/request": "8.1.6", + "@octokit/rest": "20.0.2", + "@primer/octicons": "19.8.0", "app-root-path": "3.1.0", - "applicationinsights": "2.7.0", + "applicationinsights": "2.9.2", "async-prompt": "1.0.1", - "axios": "1.4.0", + "axios": "1.6.5", "basic-auth": "2.0.1", "body-parser": "1.20.2", "color-contrast-checker": "2.1.0", @@ -34,7 +35,7 @@ "deepmerge": "4.3.1", "dotenv": "16.3.1", "express": "4.18.2", - "express-async-handler": "1.1.4", + "express-async-handler": "1.2.0", "express-session": "1.17.3", "express-sslify": "1.2.0", "file-size": "1.0.0", @@ -43,83 +44,93 @@ "js-yaml": "4.1.0", "json-2-csv": "^3.18.0", "jsonc": "2.0.0", - "jsonwebtoken": "9.0.1", - "jwks-rsa": "3.0.1", + "jsonwebtoken": "9.0.2", + "jwks-rsa": "3.1.0", "language-map": "1.5.0", "lodash": "4.17.21", - "luxon": "3.3.0", + "luxon": "3.4.4", "memory-cache": "0.2.0", - "moment": "2.29.4", + "moment": "2.30.1", "morgan": "1.10.0", "node-jose": "2.2.0", - "nodemailer": "6.9.4", + "nodemailer": "6.9.8", "object-path": "0.11.8", - "octicons": "5.0.1", - "passport": "0.6.0", + "passport": "0.7.0", "passport-azure-ad": "4.3.5", "passport-github": "1.1.0", - "pg": "8.11.0", + "pg": "8.11.3", "pg-escape": "0.2.0", "pug": "3.0.2", "pug-load": "3.0.0", "recursive-readdir": "2.2.3", - "redis": "4.6.8", + "redis": "4.6.12", "secure-compare": "3.0.1", - "semver": "7.5.1", + "semver": "7.5.4", "serve-favicon": "2.5.0", "simple-oauth2": "5.0.0", "throat": "6.0.2", "tmp-promise": "3.0.3", - "validator": "13.9.0", + "validator": "13.11.0", "walk-back": "5.1.0" }, "devDependencies": { - "@types/debug": "4.1.7", - "@types/express": "4.17.17", - "@types/express-session": "1.17.7", - "@types/jest": "29.5.1", - "@types/lodash": "4.14.194", - "@types/luxon": "3.3.0", - "@types/memory-cache": "0.2.2", - "@types/morgan": "1.9.4", - "@types/node": "20.4.2", - "@types/node-jose": "1.1.10", - "@types/object-path": "0.11.1", - "@types/passport": "1.0.12", - "@types/passport-azure-ad": "4.3.1", - "@types/passport-github": "1.1.7", - "@types/pg": "8.10.1", - "@types/pug": "2.0.6", - "@types/recursive-readdir": "2.2.1", - "@types/semver": "7.5.0", - "@types/simple-oauth2": "5.0.4", - "@types/validator": "13.7.17", - "@typescript-eslint/eslint-plugin": "5.59.7", - "@typescript-eslint/parser": "5.59.7", - "cspell": "6.31.1", - "eslint": "8.41.0", - "eslint-config-prettier": "8.8.0", - "eslint-plugin-n": "16.0.0", - "eslint-plugin-prettier": "4.2.1", + "@types/cors": "2.8.17", + "@types/debug": "4.1.12", + "@types/express": "4.17.21", + "@types/express-session": "1.17.10", + "@types/jest": "29.5.11", + "@types/lodash": "4.14.202", + "@types/luxon": "3.4.0", + "@types/memory-cache": "0.2.5", + "@types/morgan": "1.9.9", + "@types/node": "20.11.0", + "@types/node-jose": "1.1.13", + "@types/object-path": "0.11.4", + "@types/passport": "1.0.16", + "@types/passport-azure-ad": "4.3.5", + "@types/passport-github": "1.1.12", + "@types/pg": "8.10.9", + "@types/pug": "2.0.10", + "@types/recursive-readdir": "2.2.4", + "@types/semver": "7.5.6", + "@types/simple-oauth2": "5.0.7", + "@types/validator": "13.11.8", + "@typescript-eslint/eslint-plugin": "6.18.1", + "@typescript-eslint/parser": "6.18.1", + "cspell": "8.3.2", + "eslint": "8.56.0", + "eslint-config-prettier": "9.1.0", + "eslint-plugin-n": "16.6.2", + "eslint-plugin-prettier": "5.1.3", "husky": "8.0.3", - "jest": "29.5.0", + "jest": "29.7.0", "jest-junit": "16.0.0", - "lint-staged": "13.2.2", - "markdownlint-cli2": "0.7.1", - "prettier": "2.8.8", - "ts-jest": "29.1.0", - "ts-node": "10.9.1", + "lint-staged": "15.2.0", + "markdownlint-cli2": "0.12.0", + "prettier": "3.1.1", + "ts-jest": "29.1.1", + "ts-node": "10.9.2", "ts-prune": "0.10.3", - "typescript": "5.0.4" + "typescript": "5.3.3" }, "engines": { - "node": ">=16" + "node": ">=18" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" } }, "node_modules/@ampproject/remapping": { "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", "dev": true, - "license": "Apache-2.0", "dependencies": { "@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/trace-mapping": "^0.3.9" @@ -129,18 +140,20 @@ } }, "node_modules/@azure/abort-controller": { - "version": "1.0.4", - "license": "MIT", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.1.0.tgz", + "integrity": "sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw==", "dependencies": { - "tslib": "^2.0.0" + "tslib": "^2.2.0" }, "engines": { - "node": ">=8.0.0" + "node": ">=12.0.0" } }, "node_modules/@azure/core-amqp": { - "version": "3.3.0", - "license": "MIT", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@azure/core-amqp/-/core-amqp-4.1.0.tgz", + "integrity": "sha512-tjnviDypSAgjGBZCPw+sH7vDWz27N+z0xtQewp7+xH17/eb67VH4sApl3XHuxVBro6Y6pOfxCDpqFenOoGWz6Q==", "dependencies": { "@azure/abort-controller": "^1.0.0", "@azure/core-auth": "^1.3.0", @@ -148,7 +161,6 @@ "@azure/logger": "^1.0.0", "buffer": "^6.0.0", "events": "^3.0.0", - "jssha": "^3.1.0", "process": "^0.11.10", "rhea": "^3.0.0", "rhea-promise": "^3.0.0", @@ -156,57 +168,67 @@ "util": "^0.12.1" }, "engines": { - "node": ">=14.0.0" + "node": ">=18.0.0" } }, - "node_modules/@azure/core-amqp/node_modules/@azure/core-util": { - "version": "1.3.2", - "license": "MIT", + "node_modules/@azure/core-auth": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.5.0.tgz", + "integrity": "sha512-udzoBuYG1VBoHVohDTrvKjyzel34zt77Bhp7dQntVGGD0ehVq48owENbBG8fIgkHRNUBQH5k1r0hpoMu5L8+kw==", "dependencies": { "@azure/abort-controller": "^1.0.0", + "@azure/core-util": "^1.1.0", "tslib": "^2.2.0" }, "engines": { "node": ">=14.0.0" } }, - "node_modules/@azure/core-asynciterator-polyfill": { - "version": "1.0.2", - "license": "MIT", - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/@azure/core-auth": { - "version": "1.4.0", - "license": "MIT", + "node_modules/@azure/core-client": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.7.3.tgz", + "integrity": "sha512-kleJ1iUTxcO32Y06dH9Pfi9K4U+Tlb111WXEnbt7R/ne+NLRwppZiTGJuTD5VVoxTMK5NTbEtm5t2vcdNCFe2g==", "dependencies": { "@azure/abort-controller": "^1.0.0", + "@azure/core-auth": "^1.4.0", + "@azure/core-rest-pipeline": "^1.9.1", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.0.0", + "@azure/logger": "^1.0.0", "tslib": "^2.2.0" }, "engines": { - "node": ">=12.0.0" + "node": ">=14.0.0" } }, - "node_modules/@azure/core-client": { - "version": "1.5.0", - "license": "MIT", + "node_modules/@azure/core-http": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@azure/core-http/-/core-http-3.0.4.tgz", + "integrity": "sha512-Fok9VVhMdxAFOtqiiAtg74fL0UJkt0z3D+ouUUxcRLzZNBioPRAMJFVxiWoJljYpXsRi4GDQHzQHDc9AiYaIUQ==", "dependencies": { "@azure/abort-controller": "^1.0.0", - "@azure/core-asynciterator-polyfill": "^1.0.0", "@azure/core-auth": "^1.3.0", - "@azure/core-rest-pipeline": "^1.5.0", "@azure/core-tracing": "1.0.0-preview.13", + "@azure/core-util": "^1.1.1", "@azure/logger": "^1.0.0", - "tslib": "^2.2.0" + "@types/node-fetch": "^2.5.0", + "@types/tunnel": "^0.0.3", + "form-data": "^4.0.0", + "node-fetch": "^2.6.7", + "process": "^0.11.10", + "tslib": "^2.2.0", + "tunnel": "^0.0.6", + "uuid": "^8.3.0", + "xml2js": "^0.5.0" }, "engines": { - "node": ">=12.0.0" + "node": ">=14.0.0" } }, "node_modules/@azure/core-http-compat": { "version": "1.3.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@azure/core-http-compat/-/core-http-compat-1.3.0.tgz", + "integrity": "sha512-ZN9avruqbQ5TxopzG3ih3KRy52n8OAbitX3fnZT5go4hzu0J+KVPSzkL+Wt3hpJpdG8WIfg1sBD1tWkgUdEpBA==", "dependencies": { "@azure/abort-controller": "^1.0.4", "@azure/core-client": "^1.3.0", @@ -216,22 +238,36 @@ "node": ">=12.0.0" } }, + "node_modules/@azure/core-http/node_modules/@azure/core-tracing": { + "version": "1.0.0-preview.13", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.0-preview.13.tgz", + "integrity": "sha512-KxDlhXyMlh2Jhj2ykX6vNEU0Vou4nHr025KoSEiz7cS3BNiHNaZcdECk/DmLkEB0as5T7b/TpRcehJ5yV6NeXQ==", + "dependencies": { + "@opentelemetry/api": "^1.0.1", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/@azure/core-lro": { - "version": "2.2.4", - "license": "MIT", + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.5.4.tgz", + "integrity": "sha512-3GJiMVH7/10bulzOKGrrLeG/uCBH/9VtxqaMcB9lIqAeamI/xYQSHJL/KcsLDuH+yTjYpro/u6D/MuRe4dN70Q==", "dependencies": { "@azure/abort-controller": "^1.0.0", - "@azure/core-tracing": "1.0.0-preview.13", + "@azure/core-util": "^1.2.0", "@azure/logger": "^1.0.0", "tslib": "^2.2.0" }, "engines": { - "node": ">=12.0.0" + "node": ">=14.0.0" } }, "node_modules/@azure/core-paging": { "version": "1.5.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@azure/core-paging/-/core-paging-1.5.0.tgz", + "integrity": "sha512-zqWdVIt+2Z+3wqxEOGzR5hXFZ8MGKK52x4vFLw8n58pR6ZfKRx3EXYTxTaYxYHc/PexPUTyimcTWFJbji9Z6Iw==", "dependencies": { "tslib": "^2.2.0" }, @@ -240,39 +276,28 @@ } }, "node_modules/@azure/core-rest-pipeline": { - "version": "1.10.1", - "license": "MIT", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.13.0.tgz", + "integrity": "sha512-a62aP/wppgmnfIkJLfcB4ssPBcH94WzrzPVJ3tlJt050zX4lfmtnvy95D3igDo3f31StO+9BgPrzvkj4aOxnoA==", "dependencies": { - "@azure/abort-controller": "^1.0.0", + "@azure/abort-controller": "^1.1.0", "@azure/core-auth": "^1.4.0", "@azure/core-tracing": "^1.0.1", - "@azure/core-util": "^1.0.0", + "@azure/core-util": "^1.3.0", "@azure/logger": "^1.0.0", - "form-data": "^4.0.0", "http-proxy-agent": "^5.0.0", "https-proxy-agent": "^5.0.0", - "tslib": "^2.2.0", - "uuid": "^8.3.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@azure/core-rest-pipeline/node_modules/@azure/core-tracing": { - "version": "1.0.1", - "license": "MIT", - "dependencies": { "tslib": "^2.2.0" }, "engines": { - "node": ">=12.0.0" + "node": ">=18.0.0" } }, "node_modules/@azure/core-tracing": { - "version": "1.0.0-preview.13", - "license": "MIT", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.1.tgz", + "integrity": "sha512-I5CGMoLtX+pI17ZdiFJZgxMJApsK6jjfm85hpgp3oazCdq5Wxgh4wMr7ge/TTWW1B5WBuvIOI1fMU/FrOAMKrw==", "dependencies": { - "@opentelemetry/api": "^1.0.1", "tslib": "^2.2.0" }, "engines": { @@ -280,21 +305,23 @@ } }, "node_modules/@azure/core-util": { - "version": "1.2.0", - "license": "MIT", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.6.1.tgz", + "integrity": "sha512-h5taHeySlsV9qxuK64KZxy4iln1BtMYlNt5jbuEFN3UFSAd1EwKg/Gjl5a6tZ/W8t6li3xPnutOx7zbDyXnPmQ==", "dependencies": { "@azure/abort-controller": "^1.0.0", "tslib": "^2.2.0" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0" } }, "node_modules/@azure/core-xml": { - "version": "1.3.3", - "license": "MIT", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@azure/core-xml/-/core-xml-1.3.4.tgz", + "integrity": "sha512-B1xI79Ur/u+KR69fGTcsMNj8KDjBSqAy0Ys6Byy4Qm1CqoUy7gCT5A7Pej0EBWRskuH6bpCwrAnosfmQEalkcg==", "dependencies": { - "fast-xml-parser": "^4.0.8", + "fast-xml-parser": "^4.2.4", "tslib": "^2.2.0" }, "engines": { @@ -302,8 +329,9 @@ } }, "node_modules/@azure/cosmos": { - "version": "3.17.3", - "license": "MIT", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@azure/cosmos/-/cosmos-4.0.0.tgz", + "integrity": "sha512-/Z27p1+FTkmjmm8jk90zi/HrczPHw2t8WecFnsnTe4xGocWl0Z4clP0YlLUTJPhRLWYa5upwD9rMvKJkS1f1kg==", "dependencies": { "@azure/abort-controller": "^1.0.0", "@azure/core-auth": "^1.3.0", @@ -323,19 +351,10 @@ "node": ">=14.0.0" } }, - "node_modules/@azure/cosmos/node_modules/@azure/core-tracing": { - "version": "1.0.1", - "license": "MIT", - "dependencies": { - "tslib": "^2.2.0" - }, - "engines": { - "node": ">=12.0.0" - } - }, "node_modules/@azure/data-tables": { "version": "13.2.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@azure/data-tables/-/data-tables-13.2.2.tgz", + "integrity": "sha512-Dq2Aq0mMMF0BPzYQKdBY/OtO7VemP/foh6z+mJpUO1hRL+65C1rGQUJf20LJHotSyU8wHb4HJzOs+Z50GXSy1w==", "dependencies": { "@azure/core-auth": "^1.3.0", "@azure/core-client": "^1.0.0", @@ -351,54 +370,34 @@ "node": ">=14.0.0" } }, - "node_modules/@azure/data-tables/node_modules/@azure/core-tracing": { - "version": "1.0.1", - "license": "MIT", - "dependencies": { - "tslib": "^2.2.0" - }, - "engines": { - "node": ">=12.0.0" - } - }, "node_modules/@azure/identity": { - "version": "3.2.2", - "license": "MIT", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.0.0.tgz", + "integrity": "sha512-gtPYxIL0kI39Dw4t3HvlbfhOdXqKD2MqDgynlklF0j728j51dcKgRo6FLX0QzpBw/1gGfLxjMXqq3nKOSQ2lmA==", "dependencies": { "@azure/abort-controller": "^1.0.0", - "@azure/core-auth": "^1.3.0", + "@azure/core-auth": "^1.5.0", "@azure/core-client": "^1.4.0", "@azure/core-rest-pipeline": "^1.1.0", "@azure/core-tracing": "^1.0.0", "@azure/core-util": "^1.0.0", "@azure/logger": "^1.0.0", - "@azure/msal-browser": "^2.32.2", - "@azure/msal-common": "^9.0.2", - "@azure/msal-node": "^1.14.6", + "@azure/msal-browser": "^3.5.0", + "@azure/msal-node": "^2.5.1", "events": "^3.0.0", "jws": "^4.0.0", "open": "^8.0.0", "stoppable": "^1.1.0", - "tslib": "^2.2.0", - "uuid": "^8.3.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@azure/identity/node_modules/@azure/core-tracing": { - "version": "1.0.1", - "license": "MIT", - "dependencies": { "tslib": "^2.2.0" }, "engines": { - "node": ">=12.0.0" + "node": ">=18.0.0" } }, "node_modules/@azure/keyvault-secrets": { "version": "4.7.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@azure/keyvault-secrets/-/keyvault-secrets-4.7.0.tgz", + "integrity": "sha512-YvlFXRQ+SI5NT4GtSFbb6HGo6prW3yzDab8tr6vga2/SjDQew3wJsCAAr/xwZz6XshFXCYEX26CDKmPf+SJKJg==", "dependencies": { "@azure/abort-controller": "^1.0.0", "@azure/core-auth": "^1.3.0", @@ -416,100 +415,72 @@ "node": ">=14.0.0" } }, - "node_modules/@azure/keyvault-secrets/node_modules/@azure/core-tracing": { - "version": "1.0.1", - "license": "MIT", - "dependencies": { - "tslib": "^2.2.0" - }, - "engines": { - "node": ">=12.0.0" - } - }, "node_modules/@azure/logger": { - "version": "1.0.3", - "license": "MIT", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.0.4.tgz", + "integrity": "sha512-ustrPY8MryhloQj7OWGe+HrYx+aoiOxzbXTtgblbV3xwCqpzUK36phH3XNHQKj3EPonyFUuDTfR3qFhTEAuZEg==", "dependencies": { "tslib": "^2.2.0" }, "engines": { - "node": ">=12.0.0" + "node": ">=14.0.0" } }, "node_modules/@azure/msal-browser": { - "version": "2.37.0", - "license": "MIT", + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-3.7.0.tgz", + "integrity": "sha512-ktDB/Gf7UDgYBJOnoIlh70lxIo4e1/D2UgHuayB4RntN1IlusfTtIVH3k8NpJMdl+38tfTXIaUoR+qlr5voZEg==", "dependencies": { - "@azure/msal-common": "13.0.0" + "@azure/msal-common": "14.6.0" }, "engines": { "node": ">=0.8.0" } }, - "node_modules/@azure/msal-browser/node_modules/@azure/msal-common": { - "version": "13.0.0", - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/@azure/msal-common": { - "version": "9.1.1", - "license": "MIT", + "version": "14.6.0", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-14.6.0.tgz", + "integrity": "sha512-AGusT/JvxdzJIYi5u0n97cmhd3pUT6UuI6rEkT5iDeT2FGcV0/EB8pk+dy6GLPpYg9vhDCuyoYrEZGd+2UeCCQ==", "engines": { "node": ">=0.8.0" } }, "node_modules/@azure/msal-node": { - "version": "1.17.2", - "license": "MIT", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-2.6.1.tgz", + "integrity": "sha512-wYwz83pWatTNWUCkTi3cAOXbchad5FnZz/pbZz7b8Z6FuEqohXcTtg6BLip9SmcjN6FlbwUdJIZYOof2v1Gnrg==", "dependencies": { - "@azure/msal-common": "13.0.0", + "@azure/msal-common": "14.6.0", "jsonwebtoken": "^9.0.0", "uuid": "^8.3.0" }, "engines": { - "node": "10 || 12 || 14 || 16 || 18" - } - }, - "node_modules/@azure/msal-node/node_modules/@azure/msal-common": { - "version": "13.0.0", - "license": "MIT", - "engines": { - "node": ">=0.8.0" + "node": ">=16" } }, "node_modules/@azure/opentelemetry-instrumentation-azure-sdk": { - "version": "1.0.0-beta.3", - "license": "MIT", + "version": "1.0.0-beta.5", + "resolved": "https://registry.npmjs.org/@azure/opentelemetry-instrumentation-azure-sdk/-/opentelemetry-instrumentation-azure-sdk-1.0.0-beta.5.tgz", + "integrity": "sha512-fsUarKQDvjhmBO4nIfaZkfNSApm1hZBzcvpNbSrXdcUBxu7lRvKsV5DnwszX7cnhLyVOW9yl1uigtRQ1yDANjA==", "dependencies": { "@azure/core-tracing": "^1.0.0", "@azure/logger": "^1.0.0", - "@opentelemetry/api": "^1.4.0", - "@opentelemetry/core": "^1.9.0", - "@opentelemetry/instrumentation": "^0.35.0", + "@opentelemetry/api": "^1.4.1", + "@opentelemetry/core": "^1.15.2", + "@opentelemetry/instrumentation": "^0.41.2", "tslib": "^2.2.0" }, "engines": { "node": ">=14.0.0" } }, - "node_modules/@azure/opentelemetry-instrumentation-azure-sdk/node_modules/@azure/core-tracing": { - "version": "1.0.1", - "license": "MIT", - "dependencies": { - "tslib": "^2.2.0" - }, - "engines": { - "node": ">=12.0.0" - } - }, "node_modules/@azure/service-bus": { - "version": "7.9.0", - "license": "MIT", + "version": "7.9.3", + "resolved": "https://registry.npmjs.org/@azure/service-bus/-/service-bus-7.9.3.tgz", + "integrity": "sha512-6g37YpVTzRHI75BkYPiB4zqbYhQHM1Jvd+sNfRCbXeRW5FLsWoXgFVTw3Jv5kkMMX1pUNtrkaQhEoeTwtm5v3w==", "dependencies": { "@azure/abort-controller": "^1.0.0", - "@azure/core-amqp": "^3.3.0", + "@azure/core-amqp": "^4.1.0", "@azure/core-auth": "^1.3.0", "@azure/core-client": "^1.0.0", "@azure/core-paging": "^1.4.0", @@ -528,33 +499,13 @@ "tslib": "^2.2.0" }, "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@azure/service-bus/node_modules/@azure/core-tracing": { - "version": "1.0.1", - "license": "MIT", - "dependencies": { - "tslib": "^2.2.0" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/@azure/service-bus/node_modules/@azure/core-util": { - "version": "1.3.2", - "license": "MIT", - "dependencies": { - "@azure/abort-controller": "^1.0.0", - "tslib": "^2.2.0" - }, - "engines": { - "node": ">=14.0.0" + "node": ">=18.0.0" } }, "node_modules/@azure/storage-blob": { - "version": "12.14.0", - "license": "MIT", + "version": "12.17.0", + "resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.17.0.tgz", + "integrity": "sha512-sM4vpsCpcCApagRW5UIjQNlNylo02my2opgp0Emi8x888hZUvJ3dN69Oq20cEGXkMUWnoCrBaB0zyS3yeB87sQ==", "dependencies": { "@azure/abort-controller": "^1.0.0", "@azure/core-http": "^3.0.0", @@ -569,44 +520,22 @@ "node": ">=14.0.0" } }, - "node_modules/@azure/storage-blob/node_modules/@azure/core-http": { - "version": "3.0.1", - "license": "MIT", - "dependencies": { - "@azure/abort-controller": "^1.0.0", - "@azure/core-auth": "^1.3.0", - "@azure/core-tracing": "1.0.0-preview.13", - "@azure/core-util": "^1.1.1", - "@azure/logger": "^1.0.0", - "@types/node-fetch": "^2.5.0", - "@types/tunnel": "^0.0.3", - "form-data": "^4.0.0", - "node-fetch": "^2.6.7", - "process": "^0.11.10", - "tslib": "^2.2.0", - "tunnel": "^0.0.6", - "uuid": "^8.3.0", - "xml2js": "^0.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@azure/storage-blob/node_modules/xml2js": { - "version": "0.5.0", - "license": "MIT", + "node_modules/@azure/storage-blob/node_modules/@azure/core-tracing": { + "version": "1.0.0-preview.13", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.0-preview.13.tgz", + "integrity": "sha512-KxDlhXyMlh2Jhj2ykX6vNEU0Vou4nHr025KoSEiz7cS3BNiHNaZcdECk/DmLkEB0as5T7b/TpRcehJ5yV6NeXQ==", "dependencies": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" + "@opentelemetry/api": "^1.0.1", + "tslib": "^2.2.0" }, "engines": { - "node": ">=4.0.0" + "node": ">=12.0.0" } }, "node_modules/@azure/storage-queue": { - "version": "12.14.0", - "resolved": "https://registry.npmjs.org/@azure/storage-queue/-/storage-queue-12.14.0.tgz", - "integrity": "sha512-1j6uxhzCcbEDVPOTNWIJ5CsLzOAU5U/bXgGZeT25fy6IghFTC1JlPGALez2CWJ9fBVj6AmSnsiBXL/77iXhSpg==", + "version": "12.16.0", + "resolved": "https://registry.npmjs.org/@azure/storage-queue/-/storage-queue-12.16.0.tgz", + "integrity": "sha512-HzwzMsNAh2PwLtx9WfXndj9elUr6duDFak5CjM6BxdWhLqf37VywPCWmEzjxuFfrq30c1T34+MjMXnN6YgqRUw==", "dependencies": { "@azure/abort-controller": "^1.0.0", "@azure/core-http": "^3.0.0", @@ -619,79 +548,117 @@ "node": ">=14.0.0" } }, - "node_modules/@azure/storage-queue/node_modules/@azure/core-http": { - "version": "3.0.1", - "license": "MIT", + "node_modules/@azure/storage-queue/node_modules/@azure/core-tracing": { + "version": "1.0.0-preview.13", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.0-preview.13.tgz", + "integrity": "sha512-KxDlhXyMlh2Jhj2ykX6vNEU0Vou4nHr025KoSEiz7cS3BNiHNaZcdECk/DmLkEB0as5T7b/TpRcehJ5yV6NeXQ==", "dependencies": { - "@azure/abort-controller": "^1.0.0", - "@azure/core-auth": "^1.3.0", - "@azure/core-tracing": "1.0.0-preview.13", - "@azure/core-util": "^1.1.1", - "@azure/logger": "^1.0.0", - "@types/node-fetch": "^2.5.0", - "@types/tunnel": "^0.0.3", - "form-data": "^4.0.0", - "node-fetch": "^2.6.7", - "process": "^0.11.10", - "tslib": "^2.2.0", - "tunnel": "^0.0.6", - "uuid": "^8.3.0", - "xml2js": "^0.5.0" + "@opentelemetry/api": "^1.0.1", + "tslib": "^2.2.0" }, "engines": { - "node": ">=14.0.0" + "node": ">=12.0.0" } }, - "node_modules/@azure/storage-queue/node_modules/xml2js": { - "version": "0.5.0", - "license": "MIT", + "node_modules/@babel/code-frame": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", + "dev": true, "dependencies": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" + "@babel/highlight": "^7.23.4", + "chalk": "^2.4.2" }, "engines": { - "node": ">=4.0.0" + "node": ">=6.9.0" } }, - "node_modules/@babel/code-frame": { - "version": "7.21.4", + "node_modules/@babel/code-frame/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==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/highlight": "^7.18.6" + "color-convert": "^1.9.0" }, "engines": { - "node": ">=6.9.0" + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/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==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/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==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/code-frame/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" } }, "node_modules/@babel/compat-data": { - "version": "7.21.7", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz", + "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==", "dev": true, - "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.21.8", + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.7.tgz", + "integrity": "sha512-+UpDgowcmqe36d4NwqvKsyPMlOLNGMsfMmQ5WGCu+siCe3t3dfe9njrzGfdN4qq+bcNUt0+Vw6haRxBOycs4dw==", "dev": true, - "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.21.4", - "@babel/generator": "^7.21.5", - "@babel/helper-compilation-targets": "^7.21.5", - "@babel/helper-module-transforms": "^7.21.5", - "@babel/helpers": "^7.21.5", - "@babel/parser": "^7.21.8", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.5", - "@babel/types": "^7.21.5", - "convert-source-map": "^1.7.0", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helpers": "^7.23.7", + "@babel/parser": "^7.23.6", + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.7", + "@babel/types": "^7.23.6", + "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", - "semver": "^6.3.0" + "json5": "^2.2.3", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -701,25 +668,22 @@ "url": "https://opencollective.com/babel" } }, - "node_modules/@babel/core/node_modules/convert-source-map": { - "version": "1.9.0", - "dev": true, - "license": "MIT" - }, "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.0", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, - "license": "ISC", "bin": { "semver": "bin/semver.js" } }, "node_modules/@babel/generator": { - "version": "7.21.5", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", + "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/types": "^7.21.5", + "@babel/types": "^7.23.6", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -729,176 +693,190 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.21.5", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", + "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.21.5", - "@babel/helper-validator-option": "^7.21.0", - "browserslist": "^4.21.3", + "@babel/compat-data": "^7.23.5", + "@babel/helper-validator-option": "^7.23.5", + "browserslist": "^4.22.2", "lru-cache": "^5.1.1", - "semver": "^6.3.0" + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-compilation-targets/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==", "dev": true, - "license": "ISC", "dependencies": { "yallist": "^3.0.2" } }, "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.0", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, - "license": "ISC", "bin": { "semver": "bin/semver.js" } }, "node_modules/@babel/helper-compilation-targets/node_modules/yallist": { "version": "3.1.1", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.21.5", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "dev": true, - "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.21.0", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/template": "^7.20.7", - "@babel/types": "^7.21.0" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-hoist-variables": { - "version": "7.18.6", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.21.4", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", + "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/types": "^7.21.4" + "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.21.5", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", + "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-environment-visitor": "^7.21.5", - "@babel/helper-module-imports": "^7.21.4", - "@babel/helper-simple-access": "^7.21.5", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.19.1", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.5", - "@babel/types": "^7.21.5" + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.20" }, "engines": { "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.21.5", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", + "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", "dev": true, - "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-simple-access": { - "version": "7.21.5", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/types": "^7.21.5" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.18.6", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.21.5", - "license": "MIT", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", + "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.19.1", - "license": "MIT", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.21.0", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", + "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", "dev": true, - "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.21.5", + "version": "7.23.8", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.8.tgz", + "integrity": "sha512-KDqYz4PiOWvDFrdHLPhKtCThtIcKVy6avWD2oG4GEvyQ+XDZwHD4YQd+H2vNMnq2rkdxsDkU82T+Vk8U/WXHRQ==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.5", - "@babel/types": "^7.21.5" + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.7", + "@babel/types": "^7.23.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.18.6", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, "engines": { @@ -907,8 +885,9 @@ }, "node_modules/@babel/highlight/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==", "dev": true, - "license": "MIT", "dependencies": { "color-convert": "^1.9.0" }, @@ -918,8 +897,9 @@ }, "node_modules/@babel/highlight/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==", "dev": true, - "license": "MIT", "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -929,39 +909,29 @@ "node": ">=4" } }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "dev": true, - "license": "MIT" - }, "node_modules/@babel/highlight/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==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.8.0" } }, "node_modules/@babel/highlight/node_modules/has-flag": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, - "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/@babel/highlight/node_modules/supports-color": { "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, - "license": "MIT", "dependencies": { "has-flag": "^3.0.0" }, @@ -970,8 +940,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.21.8", - "license": "MIT", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.6.tgz", + "integrity": "sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==", "bin": { "parser": "bin/babel-parser.js" }, @@ -981,8 +952,9 @@ }, "node_modules/@babel/plugin-syntax-async-generators": { "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -992,8 +964,9 @@ }, "node_modules/@babel/plugin-syntax-bigint": { "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -1003,8 +976,9 @@ }, "node_modules/@babel/plugin-syntax-class-properties": { "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.12.13" }, @@ -1014,8 +988,9 @@ }, "node_modules/@babel/plugin-syntax-import-meta": { "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" }, @@ -1025,8 +1000,9 @@ }, "node_modules/@babel/plugin-syntax-json-strings": { "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -1035,11 +1011,12 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.21.4", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz", + "integrity": "sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1050,8 +1027,9 @@ }, "node_modules/@babel/plugin-syntax-logical-assignment-operators": { "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" }, @@ -1061,8 +1039,9 @@ }, "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, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -1072,8 +1051,9 @@ }, "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, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" }, @@ -1083,8 +1063,9 @@ }, "node_modules/@babel/plugin-syntax-object-rest-spread": { "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -1094,8 +1075,9 @@ }, "node_modules/@babel/plugin-syntax-optional-catch-binding": { "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -1105,8 +1087,9 @@ }, "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, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -1116,8 +1099,9 @@ }, "node_modules/@babel/plugin-syntax-top-level-await": { "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", "dev": true, - "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" }, @@ -1129,11 +1113,12 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.21.4", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.23.3.tgz", + "integrity": "sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -1143,32 +1128,34 @@ } }, "node_modules/@babel/template": { - "version": "7.20.7", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.21.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.21.4", - "@babel/generator": "^7.21.5", - "@babel/helper-environment-visitor": "^7.21.5", - "@babel/helper-function-name": "^7.21.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.21.5", - "@babel/types": "^7.21.5", - "debug": "^4.1.0", + "version": "7.23.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.7.tgz", + "integrity": "sha512-tY3mM8rH9jM0YHFGyfC0/xf+SB5eKUu7HPj7/k3fpi9dAlsMc5YbQvDi0Sh2QTPXqMhyaAtzAr807TIyfQrmyg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.6", + "@babel/types": "^7.23.6", + "debug": "^4.3.1", "globals": "^11.1.0" }, "engines": { @@ -1177,18 +1164,20 @@ }, "node_modules/@babel/traverse/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==", "dev": true, - "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/@babel/types": { - "version": "7.21.5", - "license": "MIT", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.6.tgz", + "integrity": "sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==", "dependencies": { - "@babel/helper-string-parser": "^7.21.5", - "@babel/helper-validator-identifier": "^7.19.1", + "@babel/helper-string-parser": "^7.23.4", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" }, "engines": { @@ -1197,342 +1186,443 @@ }, "node_modules/@bcoe/v8-coverage": { "version": "0.2.3", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true }, "node_modules/@cspell/cspell-bundled-dicts": { - "version": "6.31.1", + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/@cspell/cspell-bundled-dicts/-/cspell-bundled-dicts-8.3.2.tgz", + "integrity": "sha512-3ubOgz1/MDixJbq//0rQ2omB3cSdhVJDviERZeiREGz4HOq84aaK1Fqbw5SjNZHvhpoq+AYXm6kJbIAH8YhKgg==", "dev": true, - "license": "MIT", "dependencies": { - "@cspell/dict-ada": "^4.0.1", - "@cspell/dict-aws": "^3.0.0", - "@cspell/dict-bash": "^4.1.1", - "@cspell/dict-companies": "^3.0.9", - "@cspell/dict-cpp": "^5.0.2", - "@cspell/dict-cryptocurrencies": "^3.0.1", + "@cspell/dict-ada": "^4.0.2", + "@cspell/dict-aws": "^4.0.1", + "@cspell/dict-bash": "^4.1.3", + "@cspell/dict-companies": "^3.0.29", + "@cspell/dict-cpp": "^5.0.10", + "@cspell/dict-cryptocurrencies": "^5.0.0", "@cspell/dict-csharp": "^4.0.2", - "@cspell/dict-css": "^4.0.5", - "@cspell/dict-dart": "^2.0.2", - "@cspell/dict-django": "^4.0.2", - "@cspell/dict-docker": "^1.1.6", + "@cspell/dict-css": "^4.0.12", + "@cspell/dict-dart": "^2.0.3", + "@cspell/dict-django": "^4.1.0", + "@cspell/dict-docker": "^1.1.7", "@cspell/dict-dotnet": "^5.0.0", - "@cspell/dict-elixir": "^4.0.2", - "@cspell/dict-en_us": "^4.3.2", - "@cspell/dict-en-common-misspellings": "^1.0.2", + "@cspell/dict-elixir": "^4.0.3", + "@cspell/dict-en_us": "^4.3.13", + "@cspell/dict-en-common-misspellings": "^2.0.0", "@cspell/dict-en-gb": "1.1.33", - "@cspell/dict-filetypes": "^3.0.0", - "@cspell/dict-fonts": "^3.0.1", + "@cspell/dict-filetypes": "^3.0.3", + "@cspell/dict-fonts": "^4.0.0", + "@cspell/dict-fsharp": "^1.0.1", "@cspell/dict-fullstack": "^3.1.5", "@cspell/dict-gaming-terms": "^1.0.4", - "@cspell/dict-git": "^2.0.0", - "@cspell/dict-golang": "^6.0.1", + "@cspell/dict-git": "^3.0.0", + "@cspell/dict-golang": "^6.0.5", "@cspell/dict-haskell": "^4.0.1", - "@cspell/dict-html": "^4.0.3", + "@cspell/dict-html": "^4.0.5", "@cspell/dict-html-symbol-entities": "^4.0.0", - "@cspell/dict-java": "^5.0.5", - "@cspell/dict-k8s": "^1.0.1", + "@cspell/dict-java": "^5.0.6", + "@cspell/dict-k8s": "^1.0.2", "@cspell/dict-latex": "^4.0.0", - "@cspell/dict-lorem-ipsum": "^3.0.0", - "@cspell/dict-lua": "^4.0.1", - "@cspell/dict-node": "^4.0.2", - "@cspell/dict-npm": "^5.0.5", - "@cspell/dict-php": "^4.0.1", - "@cspell/dict-powershell": "^5.0.1", - "@cspell/dict-public-licenses": "^2.0.2", - "@cspell/dict-python": "^4.0.2", + "@cspell/dict-lorem-ipsum": "^4.0.0", + "@cspell/dict-lua": "^4.0.3", + "@cspell/dict-makefile": "^1.0.0", + "@cspell/dict-node": "^4.0.3", + "@cspell/dict-npm": "^5.0.14", + "@cspell/dict-php": "^4.0.5", + "@cspell/dict-powershell": "^5.0.3", + "@cspell/dict-public-licenses": "^2.0.5", + "@cspell/dict-python": "^4.1.11", "@cspell/dict-r": "^2.0.1", - "@cspell/dict-ruby": "^5.0.0", + "@cspell/dict-ruby": "^5.0.2", "@cspell/dict-rust": "^4.0.1", "@cspell/dict-scala": "^5.0.0", - "@cspell/dict-software-terms": "^3.1.6", - "@cspell/dict-sql": "^2.1.0", + "@cspell/dict-software-terms": "^3.3.15", + "@cspell/dict-sql": "^2.1.3", "@cspell/dict-svelte": "^1.0.2", "@cspell/dict-swift": "^2.0.1", - "@cspell/dict-typescript": "^3.1.1", + "@cspell/dict-typescript": "^3.1.2", "@cspell/dict-vue": "^3.0.0" }, "engines": { - "node": ">=14" + "node": ">=18" + } + }, + "node_modules/@cspell/cspell-json-reporter": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/@cspell/cspell-json-reporter/-/cspell-json-reporter-8.3.2.tgz", + "integrity": "sha512-gHSz4jXMJPcxx+lOGfXhHuoyenAWQ8PVA/atHFrWYKo1LzKTbpkEkrsDnlX8QNJubc3EMH63Uy+lOIaFDVyHiQ==", + "dev": true, + "dependencies": { + "@cspell/cspell-types": "8.3.2" + }, + "engines": { + "node": ">=18" } }, "node_modules/@cspell/cspell-pipe": { - "version": "6.31.1", + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/@cspell/cspell-pipe/-/cspell-pipe-8.3.2.tgz", + "integrity": "sha512-GZmDwvQGOjQi3IjD4k9xXeVTDANczksOsgVKb3v2QZk9mR4Qj8c6Uarjd4AgSiIhu/wBliJfzr5rWFJu4X2VfQ==", "dev": true, - "license": "MIT", "engines": { - "node": ">=14" + "node": ">=18" + } + }, + "node_modules/@cspell/cspell-resolver": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/@cspell/cspell-resolver/-/cspell-resolver-8.3.2.tgz", + "integrity": "sha512-w2Tmb95bzdEz9L4W5qvsP5raZbyEzKL7N2ksU/+yh8NEJcTuExmAl/nMnb3aIk7m2b+kPHnMOcJuwfUMLmyv4A==", + "dev": true, + "dependencies": { + "global-directory": "^4.0.1" + }, + "engines": { + "node": ">=18" } }, "node_modules/@cspell/cspell-service-bus": { - "version": "6.31.1", + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/@cspell/cspell-service-bus/-/cspell-service-bus-8.3.2.tgz", + "integrity": "sha512-skTHNyVi74//W/O+f4IauDhm6twA9S2whkylonsIzPxEl4Pn3y2ZEMXNki/MWUwZfDIzKKSxlcREH61g7zCvhg==", "dev": true, - "license": "MIT", "engines": { - "node": ">=14" + "node": ">=18" } }, "node_modules/@cspell/cspell-types": { - "version": "6.31.1", + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/@cspell/cspell-types/-/cspell-types-8.3.2.tgz", + "integrity": "sha512-qS/gWd9ItOrN6ZX5pwC9lJjnBoyiAyhxYq0GUXuV892LQvwrBmECGk6KhsA1lPW7JJS7o57YTAS1jmXnmXMEpg==", "dev": true, - "license": "MIT", "engines": { - "node": ">=14" + "node": ">=18" } }, "node_modules/@cspell/dict-ada": { - "version": "4.0.1", - "dev": true, - "license": "MIT" + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@cspell/dict-ada/-/dict-ada-4.0.2.tgz", + "integrity": "sha512-0kENOWQeHjUlfyId/aCM/mKXtkEgV0Zu2RhUXCBr4hHo9F9vph+Uu8Ww2b0i5a4ZixoIkudGA+eJvyxrG1jUpA==", + "dev": true }, "node_modules/@cspell/dict-aws": { - "version": "3.0.0", - "dev": true, - "license": "MIT" + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-aws/-/dict-aws-4.0.1.tgz", + "integrity": "sha512-NXO+kTPQGqaaJKa4kO92NAXoqS+i99dQzf3/L1BxxWVSBS3/k1f3uhmqIh7Crb/n22W793lOm0D9x952BFga3Q==", + "dev": true }, "node_modules/@cspell/dict-bash": { - "version": "4.1.1", - "dev": true, - "license": "MIT" + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@cspell/dict-bash/-/dict-bash-4.1.3.tgz", + "integrity": "sha512-tOdI3QVJDbQSwPjUkOiQFhYcu2eedmX/PtEpVWg0aFps/r6AyjUQINtTgpqMYnYuq8O1QUIQqnpx21aovcgZCw==", + "dev": true }, "node_modules/@cspell/dict-companies": { - "version": "3.0.10", - "dev": true, - "license": "MIT" + "version": "3.0.31", + "resolved": "https://registry.npmjs.org/@cspell/dict-companies/-/dict-companies-3.0.31.tgz", + "integrity": "sha512-hKVpV/lcGKP4/DpEPS8P4osPvFH/YVLJaDn9cBIOH6/HSmL5LbFgJNKpMGaYRbhm2FEX56MKE3yn/MNeNYuesQ==", + "dev": true }, "node_modules/@cspell/dict-cpp": { - "version": "5.0.3", - "dev": true, - "license": "MIT" + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-cpp/-/dict-cpp-5.1.1.tgz", + "integrity": "sha512-Qy9fNsR/5RcQ6G85gDKFjvzh0AdgAilLQeSXPtqY21Fx1kCjUqdVVJYMmHUREgcxH6ptAxtn5knTWU4PIhQtOw==", + "dev": true }, "node_modules/@cspell/dict-cryptocurrencies": { - "version": "3.0.1", - "dev": true, - "license": "MIT" + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-cryptocurrencies/-/dict-cryptocurrencies-5.0.0.tgz", + "integrity": "sha512-Z4ARIw5+bvmShL+4ZrhDzGhnc9znaAGHOEMaB/GURdS/jdoreEDY34wdN0NtdLHDO5KO7GduZnZyqGdRoiSmYA==", + "dev": true }, "node_modules/@cspell/dict-csharp": { "version": "4.0.2", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@cspell/dict-csharp/-/dict-csharp-4.0.2.tgz", + "integrity": "sha512-1JMofhLK+4p4KairF75D3A924m5ERMgd1GvzhwK2geuYgd2ZKuGW72gvXpIV7aGf52E3Uu1kDXxxGAiZ5uVG7g==", + "dev": true }, "node_modules/@cspell/dict-css": { - "version": "4.0.6", - "dev": true, - "license": "MIT" + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/@cspell/dict-css/-/dict-css-4.0.12.tgz", + "integrity": "sha512-vGBgPM92MkHQF5/2jsWcnaahOZ+C6OE/fPvd5ScBP72oFY9tn5GLuomcyO0z8vWCr2e0nUSX1OGimPtcQAlvSw==", + "dev": true }, "node_modules/@cspell/dict-dart": { - "version": "2.0.2", - "dev": true, - "license": "MIT" + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@cspell/dict-dart/-/dict-dart-2.0.3.tgz", + "integrity": "sha512-cLkwo1KT5CJY5N5RJVHks2genFkNCl/WLfj+0fFjqNR+tk3tBI1LY7ldr9piCtSFSm4x9pO1x6IV3kRUY1lLiw==", + "dev": true + }, + "node_modules/@cspell/dict-data-science": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@cspell/dict-data-science/-/dict-data-science-1.0.11.tgz", + "integrity": "sha512-TaHAZRVe0Zlcc3C23StZqqbzC0NrodRwoSAc8dis+5qLeLLnOCtagYQeROQvDlcDg3X/VVEO9Whh4W/z4PAmYQ==", + "dev": true }, "node_modules/@cspell/dict-django": { - "version": "4.0.2", - "dev": true, - "license": "MIT" + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-django/-/dict-django-4.1.0.tgz", + "integrity": "sha512-bKJ4gPyrf+1c78Z0Oc4trEB9MuhcB+Yg+uTTWsvhY6O2ncFYbB/LbEZfqhfmmuK/XJJixXfI1laF2zicyf+l0w==", + "dev": true }, "node_modules/@cspell/dict-docker": { - "version": "1.1.6", - "dev": true, - "license": "MIT" + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@cspell/dict-docker/-/dict-docker-1.1.7.tgz", + "integrity": "sha512-XlXHAr822euV36GGsl2J1CkBIVg3fZ6879ZOg5dxTIssuhUOCiV2BuzKZmt6aIFmcdPmR14+9i9Xq+3zuxeX0A==", + "dev": true }, "node_modules/@cspell/dict-dotnet": { "version": "5.0.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@cspell/dict-dotnet/-/dict-dotnet-5.0.0.tgz", + "integrity": "sha512-EOwGd533v47aP5QYV8GlSSKkmM9Eq8P3G/eBzSpH3Nl2+IneDOYOBLEUraHuiCtnOkNsz0xtZHArYhAB2bHWAw==", + "dev": true }, "node_modules/@cspell/dict-elixir": { "version": "4.0.3", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@cspell/dict-elixir/-/dict-elixir-4.0.3.tgz", + "integrity": "sha512-g+uKLWvOp9IEZvrIvBPTr/oaO6619uH/wyqypqvwpmnmpjcfi8+/hqZH8YNKt15oviK8k4CkINIqNhyndG9d9Q==", + "dev": true }, "node_modules/@cspell/dict-en_us": { - "version": "4.3.2", - "dev": true, - "license": "MIT" + "version": "4.3.13", + "resolved": "https://registry.npmjs.org/@cspell/dict-en_us/-/dict-en_us-4.3.13.tgz", + "integrity": "sha512-T6lHiGCjloGNE0d8CogF+efJZPCAP8zdzn+KnlI0Bmjaz5nvG2LTX7CXl1zkOl1nYYev0FuIk9WJ9YPVRjcFbQ==", + "dev": true }, "node_modules/@cspell/dict-en-common-misspellings": { - "version": "1.0.2", - "dev": true, - "license": "MIT" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-en-common-misspellings/-/dict-en-common-misspellings-2.0.0.tgz", + "integrity": "sha512-NOg8dlv37/YqLkCfBs5OXeJm/Wcfb/CzeOmOZJ2ZXRuxwsNuolb4TREUce0yAXRqMhawahY5TSDRJJBgKjBOdw==", + "dev": true }, "node_modules/@cspell/dict-en-gb": { "version": "1.1.33", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@cspell/dict-en-gb/-/dict-en-gb-1.1.33.tgz", + "integrity": "sha512-tKSSUf9BJEV+GJQAYGw5e+ouhEe2ZXE620S7BLKe3ZmpnjlNG9JqlnaBhkIMxKnNFkLY2BP/EARzw31AZnOv4g==", + "dev": true }, "node_modules/@cspell/dict-filetypes": { - "version": "3.0.0", - "dev": true, - "license": "MIT" + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@cspell/dict-filetypes/-/dict-filetypes-3.0.3.tgz", + "integrity": "sha512-J9UP+qwwBLfOQ8Qg9tAsKtSY/WWmjj21uj6zXTI9hRLD1eG1uUOLcfVovAmtmVqUWziPSKMr87F6SXI3xmJXgw==", + "dev": true }, "node_modules/@cspell/dict-fonts": { - "version": "3.0.2", - "dev": true, - "license": "MIT" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-fonts/-/dict-fonts-4.0.0.tgz", + "integrity": "sha512-t9V4GeN/m517UZn63kZPUYP3OQg5f0OBLSd3Md5CU3eH1IFogSvTzHHnz4Wqqbv8NNRiBZ3HfdY/pqREZ6br3Q==", + "dev": true + }, + "node_modules/@cspell/dict-fsharp": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-fsharp/-/dict-fsharp-1.0.1.tgz", + "integrity": "sha512-23xyPcD+j+NnqOjRHgW3IU7Li912SX9wmeefcY0QxukbAxJ/vAN4rBpjSwwYZeQPAn3fxdfdNZs03fg+UM+4yQ==", + "dev": true }, "node_modules/@cspell/dict-fullstack": { "version": "3.1.5", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@cspell/dict-fullstack/-/dict-fullstack-3.1.5.tgz", + "integrity": "sha512-6ppvo1dkXUZ3fbYn/wwzERxCa76RtDDl5Afzv2lijLoijGGUw5yYdLBKJnx8PJBGNLh829X352ftE7BElG4leA==", + "dev": true }, "node_modules/@cspell/dict-gaming-terms": { - "version": "1.0.4", - "dev": true, - "license": "MIT" + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@cspell/dict-gaming-terms/-/dict-gaming-terms-1.0.5.tgz", + "integrity": "sha512-C3riccZDD3d9caJQQs1+MPfrUrQ+0KHdlj9iUR1QD92FgTOF6UxoBpvHUUZ9YSezslcmpFQK4xQQ5FUGS7uWfw==", + "dev": true }, "node_modules/@cspell/dict-git": { - "version": "2.0.0", - "dev": true, - "license": "MIT" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-git/-/dict-git-3.0.0.tgz", + "integrity": "sha512-simGS/lIiXbEaqJu9E2VPoYW1OTC2xrwPPXNXFMa2uo/50av56qOuaxDrZ5eH1LidFXwoc8HROCHYeKoNrDLSw==", + "dev": true }, "node_modules/@cspell/dict-golang": { - "version": "6.0.1", - "dev": true, - "license": "MIT" + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/@cspell/dict-golang/-/dict-golang-6.0.5.tgz", + "integrity": "sha512-w4mEqGz4/wV+BBljLxduFNkMrd3rstBNDXmoX5kD4UTzIb4Sy0QybWCtg2iVT+R0KWiRRA56QKOvBsgXiddksA==", + "dev": true }, "node_modules/@cspell/dict-haskell": { "version": "4.0.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@cspell/dict-haskell/-/dict-haskell-4.0.1.tgz", + "integrity": "sha512-uRrl65mGrOmwT7NxspB4xKXFUenNC7IikmpRZW8Uzqbqcu7ZRCUfstuVH7T1rmjRgRkjcIjE4PC11luDou4wEQ==", + "dev": true }, "node_modules/@cspell/dict-html": { - "version": "4.0.3", - "dev": true, - "license": "MIT" + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@cspell/dict-html/-/dict-html-4.0.5.tgz", + "integrity": "sha512-p0brEnRybzSSWi8sGbuVEf7jSTDmXPx7XhQUb5bgG6b54uj+Z0Qf0V2n8b/LWwIPJNd1GygaO9l8k3HTCy1h4w==", + "dev": true }, "node_modules/@cspell/dict-html-symbol-entities": { "version": "4.0.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@cspell/dict-html-symbol-entities/-/dict-html-symbol-entities-4.0.0.tgz", + "integrity": "sha512-HGRu+48ErJjoweR5IbcixxETRewrBb0uxQBd6xFGcxbEYCX8CnQFTAmKI5xNaIt2PKaZiJH3ijodGSqbKdsxhw==", + "dev": true }, "node_modules/@cspell/dict-java": { - "version": "5.0.5", - "dev": true, - "license": "MIT" + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@cspell/dict-java/-/dict-java-5.0.6.tgz", + "integrity": "sha512-kdE4AHHHrixyZ5p6zyms1SLoYpaJarPxrz8Tveo6gddszBVVwIUZ+JkQE1bWNLK740GWzIXdkznpUfw1hP9nXw==", + "dev": true }, "node_modules/@cspell/dict-k8s": { - "version": "1.0.1", - "dev": true, - "license": "MIT" + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@cspell/dict-k8s/-/dict-k8s-1.0.2.tgz", + "integrity": "sha512-tLT7gZpNPnGa+IIFvK9SP1LrSpPpJ94a/DulzAPOb1Q2UBFwdpFd82UWhio0RNShduvKG/WiMZf/wGl98pn+VQ==", + "dev": true }, "node_modules/@cspell/dict-latex": { "version": "4.0.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@cspell/dict-latex/-/dict-latex-4.0.0.tgz", + "integrity": "sha512-LPY4y6D5oI7D3d+5JMJHK/wxYTQa2lJMSNxps2JtuF8hbAnBQb3igoWEjEbIbRRH1XBM0X8dQqemnjQNCiAtxQ==", + "dev": true }, "node_modules/@cspell/dict-lorem-ipsum": { - "version": "3.0.0", - "dev": true, - "license": "MIT" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-lorem-ipsum/-/dict-lorem-ipsum-4.0.0.tgz", + "integrity": "sha512-1l3yjfNvMzZPibW8A7mQU4kTozwVZVw0AvFEdy+NcqtbxH+TvbSkNMqROOFWrkD2PjnKG0+Ea0tHI2Pi6Gchnw==", + "dev": true }, "node_modules/@cspell/dict-lua": { - "version": "4.0.1", - "dev": true, - "license": "MIT" + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@cspell/dict-lua/-/dict-lua-4.0.3.tgz", + "integrity": "sha512-lDHKjsrrbqPaea13+G9s0rtXjMO06gPXPYRjRYawbNmo4E/e3XFfVzeci3OQDQNDmf2cPOwt9Ef5lu2lDmwfJg==", + "dev": true + }, + "node_modules/@cspell/dict-makefile": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-makefile/-/dict-makefile-1.0.0.tgz", + "integrity": "sha512-3W9tHPcSbJa6s0bcqWo6VisEDTSN5zOtDbnPabF7rbyjRpNo0uHXHRJQF8gAbFzoTzBBhgkTmrfSiuyQm7vBUQ==", + "dev": true }, "node_modules/@cspell/dict-node": { - "version": "4.0.2", - "dev": true, - "license": "MIT" + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@cspell/dict-node/-/dict-node-4.0.3.tgz", + "integrity": "sha512-sFlUNI5kOogy49KtPg8SMQYirDGIAoKBO3+cDLIwD4MLdsWy1q0upc7pzGht3mrjuyMiPRUV14Bb0rkVLrxOhg==", + "dev": true }, "node_modules/@cspell/dict-npm": { - "version": "5.0.5", - "dev": true, - "license": "MIT" + "version": "5.0.15", + "resolved": "https://registry.npmjs.org/@cspell/dict-npm/-/dict-npm-5.0.15.tgz", + "integrity": "sha512-sX0X5YWNW54F4baW7b5JJB6705OCBIZtUqjOghlJNORS5No7QY1IX1zc5FxNNu4gsaCZITAmfMi4ityXEsEThA==", + "dev": true }, "node_modules/@cspell/dict-php": { - "version": "4.0.1", - "dev": true, - "license": "MIT" + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@cspell/dict-php/-/dict-php-4.0.5.tgz", + "integrity": "sha512-9r8ao7Z/mH9Z8pSB7yLtyvcCJWw+/MnQpj7xGVYzIV7V2ZWDRjXZAMgteHMJ37m8oYz64q5d4tiipD300QSetQ==", + "dev": true }, "node_modules/@cspell/dict-powershell": { - "version": "5.0.1", - "dev": true, - "license": "MIT" + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@cspell/dict-powershell/-/dict-powershell-5.0.3.tgz", + "integrity": "sha512-lEdzrcyau6mgzu1ie98GjOEegwVHvoaWtzQnm1ie4DyZgMr+N6D0Iyj1lzvtmt0snvsDFa5F2bsYzf3IMKcpcA==", + "dev": true }, "node_modules/@cspell/dict-public-licenses": { - "version": "2.0.2", - "dev": true, - "license": "MIT" + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@cspell/dict-public-licenses/-/dict-public-licenses-2.0.5.tgz", + "integrity": "sha512-91HK4dSRri/HqzAypHgduRMarJAleOX5NugoI8SjDLPzWYkwZ1ftuCXSk+fy8DLc3wK7iOaFcZAvbjmnLhVs4A==", + "dev": true }, "node_modules/@cspell/dict-python": { - "version": "4.0.4", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@cspell/dict-python/-/dict-python-4.1.11.tgz", + "integrity": "sha512-XG+v3PumfzUW38huSbfT15Vqt3ihNb462ulfXifpQllPok5OWynhszCLCRQjQReV+dgz784ST4ggRxW452/kVg==", "dev": true, - "license": "MIT" + "dependencies": { + "@cspell/dict-data-science": "^1.0.11" + } }, "node_modules/@cspell/dict-r": { "version": "2.0.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@cspell/dict-r/-/dict-r-2.0.1.tgz", + "integrity": "sha512-KCmKaeYMLm2Ip79mlYPc8p+B2uzwBp4KMkzeLd5E6jUlCL93Y5Nvq68wV5fRLDRTf7N1LvofkVFWfDcednFOgA==", + "dev": true }, "node_modules/@cspell/dict-ruby": { - "version": "5.0.0", - "dev": true, - "license": "MIT" + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@cspell/dict-ruby/-/dict-ruby-5.0.2.tgz", + "integrity": "sha512-cIh8KTjpldzFzKGgrqUX4bFyav5lC52hXDKo4LbRuMVncs3zg4hcSf4HtURY+f2AfEZzN6ZKzXafQpThq3dl2g==", + "dev": true }, "node_modules/@cspell/dict-rust": { - "version": "4.0.1", - "dev": true, - "license": "MIT" + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@cspell/dict-rust/-/dict-rust-4.0.2.tgz", + "integrity": "sha512-RhziKDrklzOntxAbY3AvNR58wnFGIo3YS8+dNeLY36GFuWOvXDHFStYw5Pod4f/VXbO/+1tXtywCC4zWfB2p1w==", + "dev": true }, "node_modules/@cspell/dict-scala": { "version": "5.0.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@cspell/dict-scala/-/dict-scala-5.0.0.tgz", + "integrity": "sha512-ph0twaRoV+ylui022clEO1dZ35QbeEQaKTaV2sPOsdwIokABPIiK09oWwGK9qg7jRGQwVaRPEq0Vp+IG1GpqSQ==", + "dev": true }, "node_modules/@cspell/dict-software-terms": { - "version": "3.1.8", - "dev": true, - "license": "MIT" + "version": "3.3.16", + "resolved": "https://registry.npmjs.org/@cspell/dict-software-terms/-/dict-software-terms-3.3.16.tgz", + "integrity": "sha512-ixorEP80LGxAU+ODVSn/CYIDjV0XAlZ2VrBu7CT+PwUFJ7h8o3JX1ywKB4qnt0hHru3JjWFtBoBThmZdrXnREQ==", + "dev": true }, "node_modules/@cspell/dict-sql": { - "version": "2.1.0", - "dev": true, - "license": "MIT" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@cspell/dict-sql/-/dict-sql-2.1.3.tgz", + "integrity": "sha512-SEyTNKJrjqD6PAzZ9WpdSu6P7wgdNtGV2RV8Kpuw1x6bV+YsSptuClYG+JSdRExBTE6LwIe1bTklejUp3ZP8TQ==", + "dev": true }, "node_modules/@cspell/dict-svelte": { "version": "1.0.2", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@cspell/dict-svelte/-/dict-svelte-1.0.2.tgz", + "integrity": "sha512-rPJmnn/GsDs0btNvrRBciOhngKV98yZ9SHmg8qI6HLS8hZKvcXc0LMsf9LLuMK1TmS2+WQFAan6qeqg6bBxL2Q==", + "dev": true }, "node_modules/@cspell/dict-swift": { "version": "2.0.1", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@cspell/dict-swift/-/dict-swift-2.0.1.tgz", + "integrity": "sha512-gxrCMUOndOk7xZFmXNtkCEeroZRnS2VbeaIPiymGRHj5H+qfTAzAKxtv7jJbVA3YYvEzWcVE2oKDP4wcbhIERw==", + "dev": true }, "node_modules/@cspell/dict-typescript": { - "version": "3.1.1", - "dev": true, - "license": "MIT" + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@cspell/dict-typescript/-/dict-typescript-3.1.2.tgz", + "integrity": "sha512-lcNOYWjLUvDZdLa0UMNd/LwfVdxhE9rKA+agZBGjL3lTA3uNvH7IUqSJM/IXhJoBpLLMVEOk8v1N9xi+vDuCdA==", + "dev": true }, "node_modules/@cspell/dict-vue": { "version": "3.0.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@cspell/dict-vue/-/dict-vue-3.0.0.tgz", + "integrity": "sha512-niiEMPWPV9IeRBRzZ0TBZmNnkK3olkOPYxC1Ny2AX4TGlYRajcW0WUtoSHmvvjZNfWLSg2L6ruiBeuPSbjnG6A==", + "dev": true }, "node_modules/@cspell/dynamic-import": { - "version": "6.31.1", + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/@cspell/dynamic-import/-/dynamic-import-8.3.2.tgz", + "integrity": "sha512-4t0xM5luA3yQhar2xWvYK4wQSDB2r0u8XkpzzJqd57MnJXd7uIAxI0awGUrDXukadRaCo0tDIlMUBemH48SNVg==", "dev": true, - "license": "MIT", "dependencies": { - "import-meta-resolve": "^2.2.2" + "import-meta-resolve": "^4.0.0" }, "engines": { - "node": ">=14" + "node": ">=18.0" } }, "node_modules/@cspell/strong-weak-map": { - "version": "6.31.1", + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/@cspell/strong-weak-map/-/strong-weak-map-8.3.2.tgz", + "integrity": "sha512-Mte/2000ap278kRYOUhiGWI7MNr1+A7WSWJmlcdP4CAH5SO20sZI3/cyZLjJJEyapdhK5vaP1L5J9sUcVDHd3A==", "dev": true, - "license": "MIT", "engines": { - "node": ">=14.6" + "node": ">=18" } }, "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==", "dev": true, - "license": "MIT", "dependencies": { "@jridgewell/trace-mapping": "0.3.9" }, @@ -1542,8 +1632,9 @@ }, "node_modules/@cspotcode/source-map-support/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==", "dev": true, - "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -1551,8 +1642,9 @@ }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", "dev": true, - "license": "MIT", "dependencies": { "eslint-visitor-keys": "^3.3.0" }, @@ -1564,21 +1656,23 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.5.1", + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", "dev": true, - "license": "MIT", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, "node_modules/@eslint/eslintrc": { - "version": "2.0.3", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, - "license": "MIT", "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.5.2", + "espree": "^9.6.0", "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", @@ -1593,47 +1687,77 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "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": "8.41.0", + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", + "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", "dev": true, - "license": "MIT", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/@hapi/boom": { "version": "10.0.1", - "license": "BSD-3-Clause", + "resolved": "https://registry.npmjs.org/@hapi/boom/-/boom-10.0.1.tgz", + "integrity": "sha512-ERcCZaEjdH3OgSJlyjVk8pHIFeus91CjKP3v+MpgBNp5IvGzP2l/bRiD78nqYcKPaZdbKkK5vDBVPd2ohHBlsA==", "dependencies": { "@hapi/hoek": "^11.0.2" } }, "node_modules/@hapi/boom/node_modules/@hapi/hoek": { - "version": "11.0.2", - "license": "BSD-3-Clause" + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-11.0.4.tgz", + "integrity": "sha512-PnsP5d4q7289pS2T2EgGz147BFJ2Jpb4yrEdkpz2IhgEUzos1S7HTl7ezWh1yfYzYlj89KzLdCRkqsP6SIryeQ==" }, "node_modules/@hapi/bourne": { "version": "3.0.0", - "license": "BSD-3-Clause" + "resolved": "https://registry.npmjs.org/@hapi/bourne/-/bourne-3.0.0.tgz", + "integrity": "sha512-Waj1cwPXJDucOib4a3bAISsKJVb15MKi9IvmTI/7ssVEm6sywXGjVJDhl6/umt1pK1ZS7PacXU3A1PmFKHEZ2w==" }, "node_modules/@hapi/hoek": { "version": "10.0.1", - "license": "BSD-3-Clause" + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-10.0.1.tgz", + "integrity": "sha512-CvlW7jmOhWzuqOqiJQ3rQVLMcREh0eel4IBnxDx2FAcK8g7qoJRQK4L1CPBASoCY6y8e6zuCy3f2g+HWdkzcMw==" }, "node_modules/@hapi/topo": { "version": "5.1.0", - "license": "BSD-3-Clause", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", + "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", "dependencies": { "@hapi/hoek": "^9.0.0" } }, "node_modules/@hapi/topo/node_modules/@hapi/hoek": { "version": "9.3.0", - "license": "BSD-3-Clause" + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" }, "node_modules/@hapi/wreck": { "version": "18.0.1", - "license": "BSD-3-Clause", + "resolved": "https://registry.npmjs.org/@hapi/wreck/-/wreck-18.0.1.tgz", + "integrity": "sha512-OLHER70+rZxvDl75xq3xXOfd3e8XIvz8fWY0dqg92UvhZ29zo24vQgfqgHSYhB5ZiuFpSLeriOisAlxAo/1jWg==", "dependencies": { "@hapi/boom": "^10.0.1", "@hapi/bourne": "^3.0.0", @@ -1641,26 +1765,51 @@ } }, "node_modules/@hapi/wreck/node_modules/@hapi/hoek": { - "version": "11.0.2", - "license": "BSD-3-Clause" + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-11.0.4.tgz", + "integrity": "sha512-PnsP5d4q7289pS2T2EgGz147BFJ2Jpb4yrEdkpz2IhgEUzos1S7HTl7ezWh1yfYzYlj89KzLdCRkqsP6SIryeQ==" }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.8", + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", "dev": true, - "license": "Apache-2.0", "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", "minimatch": "^3.0.5" }, "engines": { "node": ">=10.10.0" } }, + "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@humanwhocodes/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/@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, - "license": "Apache-2.0", "engines": { "node": ">=12.22" }, @@ -1670,14 +1819,85 @@ } }, "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", + "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", + "dev": true + }, + "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-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, - "license": "BSD-3-Clause" + "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==", + "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/@istanbuljs/load-nyc-config": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", "dev": true, - "license": "ISC", "dependencies": { "camelcase": "^5.3.1", "find-up": "^4.1.0", @@ -1691,16 +1911,18 @@ }, "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, - "license": "MIT", "dependencies": { "sprintf-js": "~1.0.2" } }, "node_modules/@istanbuljs/load-nyc-config/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==", "dev": true, - "license": "MIT", "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" @@ -1711,8 +1933,9 @@ }, "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", "dev": true, - "license": "MIT", "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -1723,8 +1946,9 @@ }, "node_modules/@istanbuljs/load-nyc-config/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==", "dev": true, - "license": "MIT", "dependencies": { "p-locate": "^4.1.0" }, @@ -1734,8 +1958,9 @@ }, "node_modules/@istanbuljs/load-nyc-config/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==", "dev": true, - "license": "MIT", "dependencies": { "p-try": "^2.0.0" }, @@ -1748,8 +1973,9 @@ }, "node_modules/@istanbuljs/load-nyc-config/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==", "dev": true, - "license": "MIT", "dependencies": { "p-limit": "^2.2.0" }, @@ -1759,59 +1985,111 @@ }, "node_modules/@istanbuljs/schema": { "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/@jest/console": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", "dev": true, - "license": "MIT", "dependencies": { - "@jest/types": "^29.5.0", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", - "jest-message-util": "^29.5.0", - "jest-util": "^29.5.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", "slash": "^3.0.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/@jest/console/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/@jest/console/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/@jest/console/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/@jest/console/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/@jest/core": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", "dev": true, - "license": "MIT", "dependencies": { - "@jest/console": "^29.5.0", - "@jest/reporters": "^29.5.0", - "@jest/test-result": "^29.5.0", - "@jest/transform": "^29.5.0", - "@jest/types": "^29.5.0", + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", "@types/node": "*", "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", "ci-info": "^3.2.0", "exit": "^0.1.2", "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.5.0", - "jest-config": "^29.5.0", - "jest-haste-map": "^29.5.0", - "jest-message-util": "^29.5.0", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.5.0", - "jest-resolve-dependencies": "^29.5.0", - "jest-runner": "^29.5.0", - "jest-runtime": "^29.5.0", - "jest-snapshot": "^29.5.0", - "jest-util": "^29.5.0", - "jest-validate": "^29.5.0", - "jest-watcher": "^29.5.0", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", "micromatch": "^4.0.4", - "pretty-format": "^29.5.0", + "pretty-format": "^29.7.0", "slash": "^3.0.0", "strip-ansi": "^6.0.0" }, @@ -1827,84 +2105,160 @@ } } }, + "node_modules/@jest/core/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/@jest/core/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/@jest/core/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/@jest/core/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/@jest/core/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/@jest/core/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/@jest/environment": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", "dev": true, - "license": "MIT", "dependencies": { - "@jest/fake-timers": "^29.5.0", - "@jest/types": "^29.5.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", "@types/node": "*", - "jest-mock": "^29.5.0" + "jest-mock": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/expect": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", "dev": true, - "license": "MIT", "dependencies": { - "expect": "^29.5.0", - "jest-snapshot": "^29.5.0" + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/expect-utils": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", "dev": true, - "license": "MIT", "dependencies": { - "jest-get-type": "^29.4.3" + "jest-get-type": "^29.6.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/fake-timers": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", "dev": true, - "license": "MIT", "dependencies": { - "@jest/types": "^29.5.0", + "@jest/types": "^29.6.3", "@sinonjs/fake-timers": "^10.0.2", "@types/node": "*", - "jest-message-util": "^29.5.0", - "jest-mock": "^29.5.0", - "jest-util": "^29.5.0" + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/globals": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", "dev": true, - "license": "MIT", "dependencies": { - "@jest/environment": "^29.5.0", - "@jest/expect": "^29.5.0", - "@jest/types": "^29.5.0", - "jest-mock": "^29.5.0" + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/reporters": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", "dev": true, - "license": "MIT", "dependencies": { "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.5.0", - "@jest/test-result": "^29.5.0", - "@jest/transform": "^29.5.0", - "@jest/types": "^29.5.0", - "@jridgewell/trace-mapping": "^0.3.15", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", "@types/node": "*", "chalk": "^4.0.0", "collect-v8-coverage": "^1.0.0", @@ -1912,13 +2266,13 @@ "glob": "^7.1.3", "graceful-fs": "^4.2.9", "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-instrument": "^6.0.0", "istanbul-lib-report": "^3.0.0", "istanbul-lib-source-maps": "^4.0.0", "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.5.0", - "jest-util": "^29.5.0", - "jest-worker": "^29.5.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", "slash": "^3.0.0", "string-length": "^4.0.1", "strip-ansi": "^6.0.0", @@ -1936,37 +2290,110 @@ } } }, - "node_modules/@jest/schemas": { - "version": "29.4.3", + "node_modules/@jest/reporters/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, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.25.16" - }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=8" } }, - "node_modules/@jest/source-map": { - "version": "29.4.3", + "node_modules/@jest/reporters/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, - "license": "MIT", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.15", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" + "color-convert": "^2.0.1" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/reporters/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/@jest/reporters/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/@jest/reporters/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/@jest/reporters/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/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/test-result": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", "dev": true, - "license": "MIT", "dependencies": { - "@jest/console": "^29.5.0", - "@jest/types": "^29.5.0", + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "collect-v8-coverage": "^1.0.0" }, @@ -1975,13 +2402,14 @@ } }, "node_modules/@jest/test-sequencer": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", "dev": true, - "license": "MIT", "dependencies": { - "@jest/test-result": "^29.5.0", + "@jest/test-result": "^29.7.0", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.5.0", + "jest-haste-map": "^29.7.0", "slash": "^3.0.0" }, "engines": { @@ -1989,21 +2417,22 @@ } }, "node_modules/@jest/transform": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", "dev": true, - "license": "MIT", "dependencies": { "@babel/core": "^7.11.6", - "@jest/types": "^29.5.0", - "@jridgewell/trace-mapping": "^0.3.15", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", "babel-plugin-istanbul": "^6.1.1", "chalk": "^4.0.0", "convert-source-map": "^2.0.0", "fast-json-stable-stringify": "^2.1.0", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.5.0", - "jest-regex-util": "^29.4.3", - "jest-util": "^29.5.0", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", "micromatch": "^4.0.4", "pirates": "^4.0.4", "slash": "^3.0.0", @@ -2013,12 +2442,75 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/@jest/transform/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/@jest/transform/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/@jest/transform/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/@jest/transform/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/@jest/transform/node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, "node_modules/@jest/types": { - "version": "29.5.0", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", "dev": true, - "license": "MIT", "dependencies": { - "@jest/schemas": "^29.4.3", + "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", "@types/node": "*", @@ -2029,10 +2521,60 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/@jest/types/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/@jest/types/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/@jest/types/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/@jest/types/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/@jridgewell/gen-mapping": { "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", "dev": true, - "license": "MIT", "dependencies": { "@jridgewell/set-array": "^1.0.1", "@jridgewell/sourcemap-codec": "^1.4.10", @@ -2043,48 +2585,49 @@ } }, "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.0", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", "dev": true, - "license": "MIT", "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/set-array": { "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", "dev": true, - "license": "MIT", "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.15", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.18", + "version": "0.3.22", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz", + "integrity": "sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw==", "dev": true, - "license": "MIT", "dependencies": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "dev": true, - "license": "MIT" - }, "node_modules/@microsoft/applicationinsights-web-snippet": { "version": "1.0.1", - "license": "MIT" + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-snippet/-/applicationinsights-web-snippet-1.0.1.tgz", + "integrity": "sha512-2IHAOaLauc8qaAitvWS+U931T+ze+7MNWrDHY47IENP5y2UA0vqJDu67kWZDdpCN1fFC77sfgfB+HV7SrKshnQ==" }, "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, - "license": "MIT", "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" @@ -2095,16 +2638,18 @@ }, "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, - "license": "MIT", "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, - "license": "MIT", "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" @@ -2114,268 +2659,279 @@ } }, "node_modules/@octokit/auth-app": { - "version": "4.0.13", - "license": "MIT", - "dependencies": { - "@octokit/auth-oauth-app": "^5.0.0", - "@octokit/auth-oauth-user": "^2.0.0", - "@octokit/request": "^6.0.0", - "@octokit/request-error": "^3.0.0", - "@octokit/types": "^9.0.0", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@octokit/auth-app/-/auth-app-6.0.3.tgz", + "integrity": "sha512-9N7IlBAKEJR3tJgPSubCxIDYGXSdc+2xbkjYpk9nCyqREnH8qEMoMhiEB1WgoA9yTFp91El92XNXAi+AjuKnfw==", + "dependencies": { + "@octokit/auth-oauth-app": "^7.0.0", + "@octokit/auth-oauth-user": "^4.0.0", + "@octokit/request": "^8.0.2", + "@octokit/request-error": "^5.0.0", + "@octokit/types": "^12.0.0", "deprecation": "^2.3.1", - "lru-cache": "^9.0.0", - "universal-github-app-jwt": "^1.1.1", + "lru-cache": "^10.0.0", + "universal-github-app-jwt": "^1.1.2", "universal-user-agent": "^6.0.0" }, "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/auth-app/node_modules/lru-cache": { - "version": "9.1.1", - "license": "ISC", - "engines": { - "node": "14 || >=16.14" + "node": ">= 18" } }, "node_modules/@octokit/auth-oauth-app": { - "version": "5.0.5", - "license": "MIT", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-app/-/auth-oauth-app-7.0.1.tgz", + "integrity": "sha512-RE0KK0DCjCHXHlQBoubwlLijXEKfhMhKm9gO56xYvFmP1QTMb+vvwRPmQLLx0V+5AvV9N9I3lr1WyTzwL3rMDg==", "dependencies": { - "@octokit/auth-oauth-device": "^4.0.0", - "@octokit/auth-oauth-user": "^2.0.0", - "@octokit/request": "^6.0.0", - "@octokit/types": "^9.0.0", + "@octokit/auth-oauth-device": "^6.0.0", + "@octokit/auth-oauth-user": "^4.0.0", + "@octokit/request": "^8.0.2", + "@octokit/types": "^12.0.0", "@types/btoa-lite": "^1.0.0", "btoa-lite": "^1.0.0", "universal-user-agent": "^6.0.0" }, "engines": { - "node": ">= 14" + "node": ">= 18" } }, "node_modules/@octokit/auth-oauth-device": { - "version": "4.0.4", - "license": "MIT", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-device/-/auth-oauth-device-6.0.1.tgz", + "integrity": "sha512-yxU0rkL65QkjbqQedgVx3gmW7YM5fF+r5uaSj9tM/cQGVqloXcqP2xK90eTyYvl29arFVCW8Vz4H/t47mL0ELw==", "dependencies": { - "@octokit/oauth-methods": "^2.0.0", - "@octokit/request": "^6.0.0", - "@octokit/types": "^9.0.0", + "@octokit/oauth-methods": "^4.0.0", + "@octokit/request": "^8.0.0", + "@octokit/types": "^12.0.0", "universal-user-agent": "^6.0.0" }, "engines": { - "node": ">= 14" + "node": ">= 18" } }, "node_modules/@octokit/auth-oauth-user": { - "version": "2.1.1", - "license": "MIT", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-user/-/auth-oauth-user-4.0.1.tgz", + "integrity": "sha512-N94wWW09d0hleCnrO5wt5MxekatqEJ4zf+1vSe8MKMrhZ7gAXKFOKrDEZW2INltvBWJCyDUELgGRv8gfErH1Iw==", "dependencies": { - "@octokit/auth-oauth-device": "^4.0.0", - "@octokit/oauth-methods": "^2.0.0", - "@octokit/request": "^6.0.0", - "@octokit/types": "^9.0.0", + "@octokit/auth-oauth-device": "^6.0.0", + "@octokit/oauth-methods": "^4.0.0", + "@octokit/request": "^8.0.2", + "@octokit/types": "^12.0.0", "btoa-lite": "^1.0.0", "universal-user-agent": "^6.0.0" }, "engines": { - "node": ">= 14" + "node": ">= 18" } }, "node_modules/@octokit/auth-token": { - "version": "3.0.3", - "license": "MIT", - "dependencies": { - "@octokit/types": "^9.0.0" - }, + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz", + "integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==", "engines": { - "node": ">= 14" + "node": ">= 18" } }, "node_modules/@octokit/core": { - "version": "4.2.1", - "license": "MIT", - "dependencies": { - "@octokit/auth-token": "^3.0.0", - "@octokit/graphql": "^5.0.0", - "@octokit/request": "^6.0.0", - "@octokit/request-error": "^3.0.0", - "@octokit/types": "^9.0.0", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.0.2.tgz", + "integrity": "sha512-cZUy1gUvd4vttMic7C0lwPed8IYXWYp8kHIMatyhY8t8n3Cpw2ILczkV5pGMPqef7v0bLo0pOHrEHarsau2Ydg==", + "dependencies": { + "@octokit/auth-token": "^4.0.0", + "@octokit/graphql": "^7.0.0", + "@octokit/request": "^8.0.2", + "@octokit/request-error": "^5.0.0", + "@octokit/types": "^12.0.0", "before-after-hook": "^2.2.0", "universal-user-agent": "^6.0.0" }, "engines": { - "node": ">= 14" + "node": ">= 18" } }, "node_modules/@octokit/endpoint": { - "version": "7.0.5", - "license": "MIT", + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.4.tgz", + "integrity": "sha512-DWPLtr1Kz3tv8L0UvXTDP1fNwM0S+z6EJpRcvH66orY6Eld4XBMCSYsaWp4xIm61jTWxK68BrR7ibO+vSDnZqw==", "dependencies": { - "@octokit/types": "^9.0.0", - "is-plain-object": "^5.0.0", + "@octokit/types": "^12.0.0", "universal-user-agent": "^6.0.0" }, "engines": { - "node": ">= 14" + "node": ">= 18" } }, "node_modules/@octokit/graphql": { - "version": "5.0.5", - "license": "MIT", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.0.2.tgz", + "integrity": "sha512-OJ2iGMtj5Tg3s6RaXH22cJcxXRi7Y3EBqbHTBRq+PQAqfaS8f/236fUrWhfSn8P4jovyzqucxme7/vWSSZBX2Q==", "dependencies": { - "@octokit/request": "^6.0.0", - "@octokit/types": "^9.0.0", + "@octokit/request": "^8.0.1", + "@octokit/types": "^12.0.0", "universal-user-agent": "^6.0.0" }, "engines": { - "node": ">= 14" + "node": ">= 18" } }, "node_modules/@octokit/oauth-authorization-url": { - "version": "5.0.0", - "license": "MIT", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@octokit/oauth-authorization-url/-/oauth-authorization-url-6.0.2.tgz", + "integrity": "sha512-CdoJukjXXxqLNK4y/VOiVzQVjibqoj/xHgInekviUJV73y/BSIcwvJ/4aNHPBPKcPWFnd4/lO9uqRV65jXhcLA==", "engines": { - "node": ">= 14" + "node": ">= 18" } }, "node_modules/@octokit/oauth-methods": { - "version": "2.0.5", - "license": "MIT", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@octokit/oauth-methods/-/oauth-methods-4.0.1.tgz", + "integrity": "sha512-1NdTGCoBHyD6J0n2WGXg9+yDLZrRNZ0moTEex/LSPr49m530WNKcCfXDghofYptr3st3eTii+EHoG5k/o+vbtw==", "dependencies": { - "@octokit/oauth-authorization-url": "^5.0.0", - "@octokit/request": "^6.2.3", - "@octokit/request-error": "^3.0.3", - "@octokit/types": "^9.0.0", + "@octokit/oauth-authorization-url": "^6.0.2", + "@octokit/request": "^8.0.2", + "@octokit/request-error": "^5.0.0", + "@octokit/types": "^12.0.0", "btoa-lite": "^1.0.0" }, "engines": { - "node": ">= 14" + "node": ">= 18" } }, "node_modules/@octokit/openapi-types": { - "version": "17.2.0", - "license": "MIT" + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-19.1.0.tgz", + "integrity": "sha512-6G+ywGClliGQwRsjvqVYpklIfa7oRPA0vyhPQG/1Feh+B+wU0vGH1JiJ5T25d3g1JZYBHzR2qefLi9x8Gt+cpw==" }, "node_modules/@octokit/plugin-paginate-graphql": { - "version": "2.0.1", - "license": "MIT", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-graphql/-/plugin-paginate-graphql-4.0.0.tgz", + "integrity": "sha512-7HcYW5tP7/Z6AETAPU14gp5H5KmCPT3hmJrS/5tO7HIgbwenYmgw4OY9Ma54FDySuxMwD+wsJlxtuGWwuZuItA==", + "engines": { + "node": ">= 18" + }, "peerDependencies": { - "@octokit/core": ">=4" + "@octokit/core": ">=5" } }, "node_modules/@octokit/plugin-paginate-rest": { - "version": "6.1.2", - "license": "MIT", + "version": "9.1.5", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.1.5.tgz", + "integrity": "sha512-WKTQXxK+bu49qzwv4qKbMMRXej1DU2gq017euWyKVudA6MldaSSQuxtz+vGbhxV4CjxpUxjZu6rM2wfc1FiWVg==", "dependencies": { - "@octokit/tsconfig": "^1.0.2", - "@octokit/types": "^9.2.3" + "@octokit/types": "^12.4.0" }, "engines": { - "node": ">= 14" + "node": ">= 18" }, "peerDependencies": { - "@octokit/core": ">=4" + "@octokit/core": ">=5" } }, "node_modules/@octokit/plugin-request-log": { - "version": "1.0.4", - "license": "MIT", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-4.0.0.tgz", + "integrity": "sha512-2uJI1COtYCq8Z4yNSnM231TgH50bRkheQ9+aH8TnZanB6QilOnx8RMD2qsnamSOXtDj0ilxvevf5fGsBhBBzKA==", + "engines": { + "node": ">= 18" + }, "peerDependencies": { - "@octokit/core": ">=3" + "@octokit/core": ">=5" } }, "node_modules/@octokit/plugin-rest-endpoint-methods": { - "version": "7.1.2", - "license": "MIT", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-10.2.0.tgz", + "integrity": "sha512-ePbgBMYtGoRNXDyKGvr9cyHjQ163PbwD0y1MkDJCpkO2YH4OeXX40c4wYHKikHGZcpGPbcRLuy0unPUuafco8Q==", "dependencies": { - "@octokit/types": "^9.2.3", - "deprecation": "^2.3.1" + "@octokit/types": "^12.3.0" }, "engines": { - "node": ">= 14" + "node": ">= 18" }, "peerDependencies": { - "@octokit/core": ">=3" + "@octokit/core": ">=5" } }, "node_modules/@octokit/request": { - "version": "6.2.5", - "license": "MIT", + "version": "8.1.6", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.1.6.tgz", + "integrity": "sha512-YhPaGml3ncZC1NfXpP3WZ7iliL1ap6tLkAp6MvbK2fTTPytzVUyUesBBogcdMm86uRYO5rHaM1xIWxigWZ17MQ==", "dependencies": { - "@octokit/endpoint": "^7.0.0", - "@octokit/request-error": "^3.0.0", - "@octokit/types": "^9.0.0", - "is-plain-object": "^5.0.0", - "node-fetch": "^2.6.7", + "@octokit/endpoint": "^9.0.0", + "@octokit/request-error": "^5.0.0", + "@octokit/types": "^12.0.0", "universal-user-agent": "^6.0.0" }, "engines": { - "node": ">= 14" + "node": ">= 18" } }, "node_modules/@octokit/request-error": { - "version": "3.0.3", - "license": "MIT", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.0.1.tgz", + "integrity": "sha512-X7pnyTMV7MgtGmiXBwmO6M5kIPrntOXdyKZLigNfQWSEQzVxR4a4vo49vJjTWX70mPndj8KhfT4Dx+2Ng3vnBQ==", "dependencies": { - "@octokit/types": "^9.0.0", + "@octokit/types": "^12.0.0", "deprecation": "^2.0.0", "once": "^1.4.0" }, "engines": { - "node": ">= 14" + "node": ">= 18" } }, "node_modules/@octokit/rest": { - "version": "19.0.11", - "license": "MIT", + "version": "20.0.2", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-20.0.2.tgz", + "integrity": "sha512-Ux8NDgEraQ/DMAU1PlAohyfBBXDwhnX2j33Z1nJNziqAfHi70PuxkFYIcIt8aIAxtRE7KVuKp8lSR8pA0J5iOQ==", "dependencies": { - "@octokit/core": "^4.2.1", - "@octokit/plugin-paginate-rest": "^6.1.2", - "@octokit/plugin-request-log": "^1.0.4", - "@octokit/plugin-rest-endpoint-methods": "^7.1.2" + "@octokit/core": "^5.0.0", + "@octokit/plugin-paginate-rest": "^9.0.0", + "@octokit/plugin-request-log": "^4.0.0", + "@octokit/plugin-rest-endpoint-methods": "^10.0.0" }, "engines": { - "node": ">= 14" + "node": ">= 18" } }, - "node_modules/@octokit/tsconfig": { - "version": "1.0.2", - "license": "MIT" - }, "node_modules/@octokit/types": { - "version": "9.2.3", - "license": "MIT", + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.4.0.tgz", + "integrity": "sha512-FLWs/AvZllw/AGVs+nJ+ELCDZZJk+kY0zMen118xhL2zD0s1etIUHm1odgjP7epxYU1ln7SZxEUWYop5bhsdgQ==", "dependencies": { - "@octokit/openapi-types": "^17.2.0" + "@octokit/openapi-types": "^19.1.0" } }, "node_modules/@opentelemetry/api": { - "version": "1.4.1", - "license": "Apache-2.0", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.7.0.tgz", + "integrity": "sha512-AdY5wvN0P2vXBi3b29hxZgSFvdhdxPB9+f0B6s//P9Q8nibRWeA3cHm8UmLpio9ABigkVHJ5NMPk+Mz8VCCyrw==", "engines": { "node": ">=8.0.0" } }, "node_modules/@opentelemetry/core": { - "version": "1.13.0", - "license": "Apache-2.0", + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.20.0.tgz", + "integrity": "sha512-lSRvk5AIdD6CtgYJcJXh0wGibQ3S/8bC2qbqKs9wK8e0K1tsWV6YkGFOqVc+jIRlCbZoIBeZzDe5UI+vb94uvg==", "dependencies": { - "@opentelemetry/semantic-conventions": "1.13.0" + "@opentelemetry/semantic-conventions": "1.20.0" }, "engines": { "node": ">=14" }, "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.5.0" + "@opentelemetry/api": ">=1.0.0 <1.8.0" } }, "node_modules/@opentelemetry/instrumentation": { - "version": "0.35.1", - "license": "Apache-2.0", - "dependencies": { - "require-in-the-middle": "^5.0.3", - "semver": "^7.3.2", + "version": "0.41.2", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.41.2.tgz", + "integrity": "sha512-rxU72E0pKNH6ae2w5+xgVYZLzc5mlxAbGzF4shxMVK8YC2QQsfN38B2GPbj0jvrKWWNUElfclQ+YTykkNg/grw==", + "dependencies": { + "@types/shimmer": "^1.0.2", + "import-in-the-middle": "1.4.2", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.1", "shimmer": "^1.2.1" }, "engines": { @@ -2386,52 +2942,86 @@ } }, "node_modules/@opentelemetry/resources": { - "version": "1.13.0", - "license": "Apache-2.0", + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.20.0.tgz", + "integrity": "sha512-nOpV0vGegSq+9ze2cEDvO3BMA5pGBhmhKZiAlj+xQZjiEjPmJtdHIuBLRvptu2ahcbFJw85gIB9BYHZOvZK1JQ==", "dependencies": { - "@opentelemetry/core": "1.13.0", - "@opentelemetry/semantic-conventions": "1.13.0" + "@opentelemetry/core": "1.20.0", + "@opentelemetry/semantic-conventions": "1.20.0" }, "engines": { "node": ">=14" }, "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.5.0" + "@opentelemetry/api": ">=1.0.0 <1.8.0" } }, "node_modules/@opentelemetry/sdk-trace-base": { - "version": "1.13.0", - "license": "Apache-2.0", + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.20.0.tgz", + "integrity": "sha512-BAIZ0hUgnhdb3OBQjn1FKGz/Iwie4l+uOMKklP7FGh7PTqEAbbzDNMJKaZQh6KepF7Fq+CZDRKslD3yrYy2Tzw==", "dependencies": { - "@opentelemetry/core": "1.13.0", - "@opentelemetry/resources": "1.13.0", - "@opentelemetry/semantic-conventions": "1.13.0" + "@opentelemetry/core": "1.20.0", + "@opentelemetry/resources": "1.20.0", + "@opentelemetry/semantic-conventions": "1.20.0" }, "engines": { "node": ">=14" }, "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.5.0" + "@opentelemetry/api": ">=1.0.0 <1.8.0" } }, "node_modules/@opentelemetry/semantic-conventions": { - "version": "1.13.0", - "license": "Apache-2.0", + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.20.0.tgz", + "integrity": "sha512-3zLJJCgTKYpbqFX8drl8hOCHtdchELC+kGqlVcV4mHW1DiElTtv1Nt9EKBptTd1IfL56QkuYnWJ3DeHd2Gtu/A==", + "engines": { + "node": ">=14" + } + }, + "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/@pkgr/core": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", + "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/@primer/octicons": { + "version": "19.8.0", + "resolved": "https://registry.npmjs.org/@primer/octicons/-/octicons-19.8.0.tgz", + "integrity": "sha512-Imze/fyW41Io5fN+27T5EAeXJrgBjMbz6nzU+wYbRylXvIAjLPUvaJPVoStiFlgSU+TjTUJqg5A9rgMDzTyMCg==", + "dependencies": { + "object-assign": "^4.1.1" + } + }, "node_modules/@redis/bloom": { "version": "1.2.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz", + "integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==", "peerDependencies": { "@redis/client": "^1.0.0" } }, "node_modules/@redis/client": { - "version": "1.5.9", - "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.5.9.tgz", - "integrity": "sha512-SffgN+P1zdWJWSXBvJeynvEnmnZrYmtKSRW00xl8pOPFOMJjxRR9u0frSxJpPR6Y4V+k54blJjGW7FgxbTI7bQ==", + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.5.13.tgz", + "integrity": "sha512-epkUM9D0Sdmt93/8Ozk43PNjLi36RZzG+d/T1Gdu5AI8jvghonTeLYV69WVWdilvFo+PYxbP0TZ0saMvr6nscQ==", "dependencies": { "cluster-key-slot": "1.1.2", "generic-pool": "3.9.0", @@ -2442,23 +3032,25 @@ } }, "node_modules/@redis/graph": { - "version": "1.1.0", - "license": "MIT", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.1.tgz", + "integrity": "sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==", "peerDependencies": { "@redis/client": "^1.0.0" } }, "node_modules/@redis/json": { - "version": "1.0.4", - "license": "MIT", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.6.tgz", + "integrity": "sha512-rcZO3bfQbm2zPRpqo82XbW8zg4G/w4W3tI7X8Mqleq9goQjAGLL7q/1n1ZX4dXEAmORVZ4s1+uKLaUOg7LrUhw==", "peerDependencies": { "@redis/client": "^1.0.0" } }, "node_modules/@redis/search": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.1.3.tgz", - "integrity": "sha512-4Dg1JjvCevdiCBTZqjhKkGoC5/BcB7k9j99kdMnaXFXg8x4eyOIVg9487CMv7/BUVkFLZCaIh8ead9mU15DNng==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.1.6.tgz", + "integrity": "sha512-mZXCxbTYKBQ3M2lZnEddwEAks0Kc7nauire8q20oA0oA/LoA+E/b5Y5KZn232ztPb1FkIGqo12vh3Lf+Vw5iTw==", "peerDependencies": { "@redis/client": "^1.0.0" } @@ -2473,55 +3065,76 @@ }, "node_modules/@sideway/address": { "version": "4.1.4", - "license": "BSD-3-Clause", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz", + "integrity": "sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==", "dependencies": { "@hapi/hoek": "^9.0.0" } }, "node_modules/@sideway/address/node_modules/@hapi/hoek": { "version": "9.3.0", - "license": "BSD-3-Clause" + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" }, "node_modules/@sideway/formula": { "version": "3.0.1", - "license": "BSD-3-Clause" + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", + "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==" }, "node_modules/@sideway/pinpoint": { "version": "2.0.0", - "license": "BSD-3-Clause" + "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", + "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" }, "node_modules/@sinclair/typebox": { - "version": "0.25.24", - "dev": true, - "license": "MIT" + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-1.0.0.tgz", + "integrity": "sha512-rUV5WyJrJLoloD4NDN1V1+LDMDWOa4OTsT4yYJwQNpTU6FWxkxHpL7eu4w+DmiH8x/EAM1otkPE1+LaspIbplw==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/@sinonjs/commons": { - "version": "2.0.0", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", + "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { "type-detect": "4.0.8" } }, "node_modules/@sinonjs/fake-timers": { - "version": "10.0.2", + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { - "@sinonjs/commons": "^2.0.0" + "@sinonjs/commons": "^3.0.0" } }, "node_modules/@tootallnate/once": { "version": "2.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", "engines": { "node": ">= 10" } }, "node_modules/@ts-morph/common": { "version": "0.12.3", + "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.12.3.tgz", + "integrity": "sha512-4tUmeLyXJnJWvTFOKtcNJ1yh0a3SsTLi2MUoyj8iUNznFRN1ZquaNe7Oukqrnki2FzZkm0J9adCNLDZxUzvj+w==", "dev": true, - "license": "MIT", "dependencies": { "fast-glob": "^3.2.7", "minimatch": "^3.0.4", @@ -2529,30 +3142,57 @@ "path-browserify": "^1.0.1" } }, + "node_modules/@ts-morph/common/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@ts-morph/common/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/@tsconfig/node10": { "version": "1.0.9", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true }, "node_modules/@tsconfig/node12": { "version": "1.0.11", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true }, "node_modules/@tsconfig/node14": { "version": "1.0.3", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true }, "node_modules/@tsconfig/node16": { - "version": "1.0.3", - "dev": true, - "license": "MIT" + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true }, "node_modules/@types/babel__core": { - "version": "7.20.0", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", "dev": true, - "license": "MIT", "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", @@ -2562,60 +3202,77 @@ } }, "node_modules/@types/babel__generator": { - "version": "7.6.4", + "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, - "license": "MIT", "dependencies": { "@babel/types": "^7.0.0" } }, "node_modules/@types/babel__template": { - "version": "7.4.1", + "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, - "license": "MIT", "dependencies": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0" } }, "node_modules/@types/babel__traverse": { - "version": "7.18.5", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.5.tgz", + "integrity": "sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==", "dev": true, - "license": "MIT", "dependencies": { - "@babel/types": "^7.3.0" + "@babel/types": "^7.20.7" } }, "node_modules/@types/body-parser": { - "version": "1.19.2", - "license": "MIT", + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", "dependencies": { "@types/connect": "*", "@types/node": "*" } }, "node_modules/@types/btoa-lite": { - "version": "1.0.0", - "license": "MIT" + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/btoa-lite/-/btoa-lite-1.0.2.tgz", + "integrity": "sha512-ZYbcE2x7yrvNFJiU7xJGrpF/ihpkM7zKgw8bha3LNJSesvTtUNxbpzaT7WXBIryf6jovisrxTBvymxMeLLj1Mg==" }, "node_modules/@types/connect": { - "version": "3.4.35", - "license": "MIT", + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/cors": { + "version": "2.8.17", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", + "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", + "dev": true, "dependencies": { "@types/node": "*" } }, "node_modules/@types/debug": { - "version": "4.1.7", + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", "dev": true, - "license": "MIT", "dependencies": { "@types/ms": "*" } }, "node_modules/@types/express": { - "version": "4.17.17", - "license": "MIT", + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^4.17.33", @@ -2624,8 +3281,9 @@ } }, "node_modules/@types/express-serve-static-core": { - "version": "4.17.34", - "license": "MIT", + "version": "4.17.41", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.41.tgz", + "integrity": "sha512-OaJ7XLaelTgrvlZD8/aa0vvvxZdUmlCn6MtWeB7TkiKW70BQLc9XEPpDLPdbo52ZhXUCrznlWdCHWxJWtdyajA==", "dependencies": { "@types/node": "*", "@types/qs": "*", @@ -2634,174 +3292,193 @@ } }, "node_modules/@types/express-session": { - "version": "1.17.7", + "version": "1.17.10", + "resolved": "https://registry.npmjs.org/@types/express-session/-/express-session-1.17.10.tgz", + "integrity": "sha512-U32bC/s0ejXijw5MAzyaV4tuZopCh/K7fPoUDyNbsRXHvPSeymygYD1RFL99YOLhF5PNOkzswvOTRaVHdL1zMw==", "dev": true, - "license": "MIT", "dependencies": { "@types/express": "*" } }, "node_modules/@types/graceful-fs": { - "version": "4.1.6", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", "dev": true, - "license": "MIT", "dependencies": { "@types/node": "*" } }, + "node_modules/@types/http-errors": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==" + }, "node_modules/@types/is-buffer": { - "version": "2.0.0", - "license": "MIT", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@types/is-buffer/-/is-buffer-2.0.2.tgz", + "integrity": "sha512-G6OXy83Va+xEo8XgqAJYOuvOMxeey9xM5XKkvwJNmN8rVdcB+r15HvHsG86hl86JvU0y1aa7Z2ERkNFYWw9ySg==", "dependencies": { "@types/node": "*" } }, "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.4", - "dev": true, - "license": "MIT" + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true }, "node_modules/@types/istanbul-lib-report": { - "version": "3.0.0", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", "dev": true, - "license": "MIT", "dependencies": { "@types/istanbul-lib-coverage": "*" } }, "node_modules/@types/istanbul-reports": { - "version": "3.0.1", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", "dev": true, - "license": "MIT", "dependencies": { "@types/istanbul-lib-report": "*" } }, "node_modules/@types/jest": { - "version": "29.5.1", + "version": "29.5.11", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.11.tgz", + "integrity": "sha512-S2mHmYIVe13vrm6q4kN6fLYYAka15ALQki/vgDC3mIukEOx8WJlv0kQPM+d4w8Gp6u0uSdKND04IlTXBv0rwnQ==", "dev": true, - "license": "MIT", "dependencies": { "expect": "^29.0.0", "pretty-format": "^29.0.0" } }, "node_modules/@types/json-schema": { - "version": "7.0.11", - "dev": true, - "license": "MIT" + "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.2", - "license": "MIT", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.5.tgz", + "integrity": "sha512-VRLSGzik+Unrup6BsouBeHsf4d1hOEgYWTm/7Nmw1sXoN1+tRly/Gy/po3yeahnP4jfnQWWAhQAqcNfH7ngOkA==", "dependencies": { "@types/node": "*" } }, "node_modules/@types/lodash": { - "version": "4.14.194", - "dev": true, - "license": "MIT" + "version": "4.14.202", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.202.tgz", + "integrity": "sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==", + "dev": true }, "node_modules/@types/luxon": { - "version": "3.3.0", - "dev": true, - "license": "MIT" + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.4.0.tgz", + "integrity": "sha512-PEVoA4MOfSsFNaPrZjIUGUZujBDxnO/tj2A2N9KfzlR+pNgpBdDuk0TmRvSMAVUP5q4q8IkMEZ8UOp3MIr+QgA==", + "dev": true }, "node_modules/@types/memory-cache": { - "version": "0.2.2", - "dev": true, - "license": "MIT" + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@types/memory-cache/-/memory-cache-0.2.5.tgz", + "integrity": "sha512-OIKRDDZUFeKT/rsko7/CGnR5qE8xTU8ogzyaaSDSHGkKwlSB/E6RopSF5fReo89khCAcGIoqSi723tDqUe6gYw==", + "dev": true }, "node_modules/@types/mime": { - "version": "1.3.2", - "license": "MIT" + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==" }, "node_modules/@types/morgan": { - "version": "1.9.4", + "version": "1.9.9", + "resolved": "https://registry.npmjs.org/@types/morgan/-/morgan-1.9.9.tgz", + "integrity": "sha512-iRYSDKVaC6FkGSpEVVIvrRGw0DfJMiQzIn3qr2G5B3C//AWkulhXgaBd7tS9/J79GWSYMTHGs7PfI5b3Y8m+RQ==", "dev": true, - "license": "MIT", "dependencies": { "@types/node": "*" } }, "node_modules/@types/ms": { - "version": "0.7.31", - "dev": true, - "license": "MIT" + "version": "0.7.34", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", + "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==", + "dev": true }, "node_modules/@types/node": { - "version": "20.4.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.2.tgz", - "integrity": "sha512-Dd0BYtWgnWJKwO1jkmTrzofjK2QXXcai0dmtzvIBhcA+RsG5h8R3xlyta0kGOZRNfL9GuRtb1knmPEhQrePCEw==" - }, - "node_modules/@types/node-fetch": { - "version": "2.6.1", - "license": "MIT", + "version": "20.11.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.0.tgz", + "integrity": "sha512-o9bjXmDNcF7GbM4CNQpmi+TutCgap/K3w1JyKgxAjqx41zp9qlIAVFi0IhCNsJcXolEqLWhbFbEeL0PvYm4pcQ==", "dependencies": { - "@types/node": "*", - "form-data": "^3.0.0" + "undici-types": "~5.26.4" } }, - "node_modules/@types/node-fetch/node_modules/form-data": { - "version": "3.0.1", - "license": "MIT", + "node_modules/@types/node-fetch": { + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.11.tgz", + "integrity": "sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==", "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" + "@types/node": "*", + "form-data": "^4.0.0" } }, "node_modules/@types/node-jose": { - "version": "1.1.10", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/@types/node-jose/-/node-jose-1.1.13.tgz", + "integrity": "sha512-QjMd4yhwy1EvSToQn0YI3cD29YhyfxFwj7NecuymjLys2/P0FwxWnkgBlFxCai6Y3aBCe7rbwmqwJJawxlgcXw==", "dev": true, - "license": "MIT", "dependencies": { "@types/node": "*" } }, "node_modules/@types/oauth": { - "version": "0.9.1", + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/@types/oauth/-/oauth-0.9.4.tgz", + "integrity": "sha512-qk9orhti499fq5XxKCCEbd0OzdPZuancneyse3KtR+vgMiHRbh+mn8M4G6t64ob/Fg+GZGpa565MF/2dKWY32A==", "dev": true, - "license": "MIT", "dependencies": { "@types/node": "*" } }, "node_modules/@types/object-path": { - "version": "0.11.1", - "dev": true, - "license": "MIT" + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/@types/object-path/-/object-path-0.11.4.tgz", + "integrity": "sha512-4tgJ1Z3elF/tOMpA8JLVuR9spt9Ynsf7+JjqsQ2IqtiPJtcLoHoXcT6qU4E10cPFqyXX5HDm9QwIzZhBSkLxsw==", + "dev": true }, "node_modules/@types/parse-json": { - "version": "4.0.0", - "dev": true, - "license": "MIT" + "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==", + "dev": true }, "node_modules/@types/passport": { - "version": "1.0.12", + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.16.tgz", + "integrity": "sha512-FD0qD5hbPWQzaM0wHUnJ/T0BBCJBxCeemtnCwc/ThhTg3x9jfrAcRUmj5Dopza+MfFS9acTe3wk7rcVnRIp/0A==", "dev": true, - "license": "MIT", "dependencies": { "@types/express": "*" } }, "node_modules/@types/passport-azure-ad": { - "version": "4.3.1", + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/@types/passport-azure-ad/-/passport-azure-ad-4.3.5.tgz", + "integrity": "sha512-dzWBDye7VvzWgKQbxImEvT0x1b0Vi37AYZrjN/XitTkstHsegDT97Wha5Aknoh4vPpv68DdaxZ4defK8YIk7kA==", "dev": true, - "license": "MIT", "dependencies": { "@types/express": "*", "@types/passport": "*" } }, "node_modules/@types/passport-github": { - "version": "1.1.7", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/@types/passport-github/-/passport-github-1.1.12.tgz", + "integrity": "sha512-VJpMEIH+cOoXB694QgcxuvWy2wPd1Oq3gqrg2Y9DMVBYs9TmH9L14qnqPDZsNMZKBDH+SvqRsGZj9SgHYeDgcA==", "dev": true, - "license": "MIT", "dependencies": { "@types/express": "*", "@types/passport": "*", @@ -2809,9 +3486,10 @@ } }, "node_modules/@types/passport-oauth2": { - "version": "1.4.11", + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@types/passport-oauth2/-/passport-oauth2-1.4.15.tgz", + "integrity": "sha512-9cUTP/HStNSZmhxXGuRrBJfEWzIEJRub2eyJu3CvkA+8HAMc9W3aKdFhVq+Qz1hi42qn+GvSAnz3zwacDSYWpw==", "dev": true, - "license": "MIT", "dependencies": { "@types/express": "*", "@types/oauth": "*", @@ -2819,175 +3497,140 @@ } }, "node_modules/@types/pg": { - "version": "8.10.1", + "version": "8.10.9", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.10.9.tgz", + "integrity": "sha512-UksbANNE/f8w0wOMxVKKIrLCbEMV+oM1uKejmwXr39olg4xqcfBDbXxObJAt6XxHbDa4XTKOlUEcEltXDX+XLQ==", "dev": true, - "license": "MIT", "dependencies": { "@types/node": "*", "pg-protocol": "*", "pg-types": "^4.0.1" } }, - "node_modules/@types/pg/node_modules/pg-types": { - "version": "4.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "pg-int8": "1.0.1", - "pg-numeric": "1.0.2", - "postgres-array": "~3.0.1", - "postgres-bytea": "~3.0.0", - "postgres-date": "~2.0.1", - "postgres-interval": "^3.0.0", - "postgres-range": "^1.1.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@types/pg/node_modules/postgres-array": { - "version": "3.0.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - } - }, - "node_modules/@types/pg/node_modules/postgres-bytea": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "obuf": "~1.1.2" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/@types/pg/node_modules/postgres-date": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - } - }, - "node_modules/@types/pg/node_modules/postgres-interval": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - } - }, - "node_modules/@types/prettier": { - "version": "2.7.2", - "dev": true, - "license": "MIT" - }, "node_modules/@types/pug": { - "version": "2.0.6", - "dev": true, - "license": "MIT" + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.10.tgz", + "integrity": "sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA==", + "dev": true }, "node_modules/@types/qs": { - "version": "6.9.7", - "license": "MIT" + "version": "6.9.11", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.11.tgz", + "integrity": "sha512-oGk0gmhnEJK4Yyk+oI7EfXsLayXatCWPHary1MtcmbAifkobT9cM9yutG/hZKIseOU0MqbIwQ/u2nn/Gb+ltuQ==" }, "node_modules/@types/range-parser": { - "version": "1.2.4", - "license": "MIT" + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==" }, "node_modules/@types/recursive-readdir": { - "version": "2.2.1", + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/@types/recursive-readdir/-/recursive-readdir-2.2.4.tgz", + "integrity": "sha512-84REEGT3lcgopvpkmGApzmU5UEG0valme5rQS/KGiguTkJ70/Au8UYZTyrzoZnY9svuX9351+1uvrRPzWDD/uw==", "dev": true, - "license": "MIT", "dependencies": { "@types/node": "*" } }, "node_modules/@types/semver": { - "version": "7.5.0", - "dev": true, - "license": "MIT" + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz", + "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==", + "dev": true }, "node_modules/@types/send": { - "version": "0.17.1", - "license": "MIT", + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", "dependencies": { "@types/mime": "^1", "@types/node": "*" } }, "node_modules/@types/serve-static": { - "version": "1.13.10", - "license": "MIT", + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.5.tgz", + "integrity": "sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==", "dependencies": { - "@types/mime": "^1", + "@types/http-errors": "*", + "@types/mime": "*", "@types/node": "*" } }, + "node_modules/@types/shimmer": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/shimmer/-/shimmer-1.0.5.tgz", + "integrity": "sha512-9Hp0ObzwwO57DpLFF0InUjUm/II8GmKAvzbefxQTihCb7KI6yc9yzf0nLc4mVdby5N4DRCgQM2wCup9KTieeww==" + }, "node_modules/@types/simple-oauth2": { - "version": "5.0.4", - "dev": true, - "license": "MIT" + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/@types/simple-oauth2/-/simple-oauth2-5.0.7.tgz", + "integrity": "sha512-8JbWVJbiTSBQP/7eiyGKyXWAqp3dKQZpaA+pdW16FCi32ujkzRMG8JfjoAzdWt6W8U591ZNdHcPtP2D7ILTKuA==", + "dev": true }, "node_modules/@types/stack-utils": { - "version": "2.0.1", - "dev": true, - "license": "MIT" + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true }, "node_modules/@types/tunnel": { "version": "0.0.3", - "license": "MIT", + "resolved": "https://registry.npmjs.org/@types/tunnel/-/tunnel-0.0.3.tgz", + "integrity": "sha512-sOUTGn6h1SfQ+gbgqC364jLFBw2lnFqkgF3q0WovEHRLMrVD1sd5aufqi/aJObLekJO+Aq5z646U4Oxy6shXMA==", "dependencies": { "@types/node": "*" } }, "node_modules/@types/validator": { - "version": "13.7.17", - "dev": true, - "license": "MIT" + "version": "13.11.8", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.11.8.tgz", + "integrity": "sha512-c/hzNDBh7eRF+KbCf+OoZxKbnkpaK/cKp9iLQWqB7muXtM+MtL9SUUH8vCFcLn6dH1Qm05jiexK0ofWY7TfOhQ==", + "dev": true }, "node_modules/@types/yargs": { - "version": "17.0.24", + "version": "17.0.32", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", + "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", "dev": true, - "license": "MIT", "dependencies": { "@types/yargs-parser": "*" } }, "node_modules/@types/yargs-parser": { - "version": "21.0.0", - "dev": true, - "license": "MIT" + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.59.7", + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.18.1.tgz", + "integrity": "sha512-nISDRYnnIpk7VCFrGcu1rnZfM1Dh9LRHnfgdkjcbi/l7g16VYRri3TjXi9Ir4lOZSw5N/gnV/3H7jIPQ8Q4daA==", "dev": true, - "license": "MIT", "dependencies": { - "@eslint-community/regexpp": "^4.4.0", - "@typescript-eslint/scope-manager": "5.59.7", - "@typescript-eslint/type-utils": "5.59.7", - "@typescript-eslint/utils": "5.59.7", + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.18.1", + "@typescript-eslint/type-utils": "6.18.1", + "@typescript-eslint/utils": "6.18.1", + "@typescript-eslint/visitor-keys": "6.18.1", "debug": "^4.3.4", - "grapheme-splitter": "^1.0.4", - "ignore": "^5.2.0", - "natural-compare-lite": "^1.4.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.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" + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -2996,24 +3639,26 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "5.59.7", + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.18.1.tgz", + "integrity": "sha512-zct/MdJnVaRRNy9e84XnVtRv9Vf91/qqe+hZJtKanjojud4wAVy/7lXxJmMyX6X6J+xc6c//YEWvpeif8cAhWA==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/scope-manager": "5.59.7", - "@typescript-eslint/types": "5.59.7", - "@typescript-eslint/typescript-estree": "5.59.7", + "@typescript-eslint/scope-manager": "6.18.1", + "@typescript-eslint/types": "6.18.1", + "@typescript-eslint/typescript-estree": "6.18.1", + "@typescript-eslint/visitor-keys": "6.18.1", "debug": "^4.3.4" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "eslint": "^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -3022,15 +3667,16 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.59.7", + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.18.1.tgz", + "integrity": "sha512-BgdBwXPFmZzaZUuw6wKiHKIovms97a7eTImjkXCZE04TGHysG+0hDQPmygyvgtkoB/aOQwSM/nWv3LzrOIQOBw==", "dev": true, - "license": "MIT", "dependencies": { - "@typescript-eslint/types": "5.59.7", - "@typescript-eslint/visitor-keys": "5.59.7" + "@typescript-eslint/types": "6.18.1", + "@typescript-eslint/visitor-keys": "6.18.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -3038,24 +3684,25 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.59.7", + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.18.1.tgz", + "integrity": "sha512-wyOSKhuzHeU/5pcRDP2G2Ndci+4g653V43gXTpt4nbyoIOAASkGDA9JIAgbQCdCkcr1MvpSYWzxTz0olCn8+/Q==", "dev": true, - "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "5.59.7", - "@typescript-eslint/utils": "5.59.7", + "@typescript-eslint/typescript-estree": "6.18.1", + "@typescript-eslint/utils": "6.18.1", "debug": "^4.3.4", - "tsutils": "^3.21.0" + "ts-api-utils": "^1.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "*" + "eslint": "^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -3064,11 +3711,12 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.59.7", + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.18.1.tgz", + "integrity": "sha512-4TuMAe+tc5oA7wwfqMtB0Y5OrREPF1GeJBAjqwgZh1lEMH5PJQgWgHGfYufVB51LtjD+peZylmeyxUXPfENLCw==", "dev": true, - "license": "MIT", "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -3076,20 +3724,22 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.59.7", + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.18.1.tgz", + "integrity": "sha512-fv9B94UAhywPRhUeeV/v+3SBDvcPiLxRZJw/xZeeGgRLQZ6rLMG+8krrJUyIf6s1ecWTzlsbp0rlw7n9sjufHA==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/types": "5.59.7", - "@typescript-eslint/visitor-keys": "5.59.7", + "@typescript-eslint/types": "6.18.1", + "@typescript-eslint/visitor-keys": "6.18.1", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -3102,49 +3752,57 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "5.59.7", + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.18.1.tgz", + "integrity": "sha512-zZmTuVZvD1wpoceHvoQpOiewmWu3uP9FuTWo8vqpy2ffsmfCE8mklRPi+vmnIYAIk9t/4kOThri2QCDgor+OpQ==", "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.59.7", - "@typescript-eslint/types": "5.59.7", - "@typescript-eslint/typescript-estree": "5.59.7", - "eslint-scope": "^5.1.1", - "semver": "^7.3.7" + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.18.1", + "@typescript-eslint/types": "6.18.1", + "@typescript-eslint/typescript-estree": "6.18.1", + "semver": "^7.5.4" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "eslint": "^7.0.0 || ^8.0.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.59.7", + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.18.1.tgz", + "integrity": "sha512-/kvt0C5lRqGoCfsbmm7/CwMqoSkY3zzHLIjdhHZQW3VFrnz7ATecOHR7nb7V+xn4286MBxfnQfQhAmCI0u+bJA==", "dev": true, - "license": "MIT", "dependencies": { - "@typescript-eslint/types": "5.59.7", - "eslint-visitor-keys": "^3.3.0" + "@typescript-eslint/types": "6.18.1", + "eslint-visitor-keys": "^3.4.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, "node_modules/accepts": { "version": "1.3.8", - "license": "MIT", + "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" @@ -3154,9 +3812,9 @@ } }, "node_modules/acorn": { - "version": "8.8.0", - "dev": true, - "license": "MIT", + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "bin": { "acorn": "bin/acorn" }, @@ -3164,25 +3822,36 @@ "node": ">=0.4.0" } }, + "node_modules/acorn-import-assertions": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", + "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", + "peerDependencies": { + "acorn": "^8" + } + }, "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, - "license": "MIT", "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "node_modules/acorn-walk": { - "version": "8.2.0", + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", + "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.4.0" } }, "node_modules/agent-base": { "version": "6.0.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", "dependencies": { "debug": "4" }, @@ -3190,22 +3859,11 @@ "node": ">= 6.0.0" } }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "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, - "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -3219,8 +3877,9 @@ }, "node_modules/ansi-escapes": { "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", "dev": true, - "license": "MIT", "dependencies": { "type-fest": "^0.21.3" }, @@ -3233,8 +3892,9 @@ }, "node_modules/ansi-escapes/node_modules/type-fest": { "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "dev": true, - "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=10" }, @@ -3243,22 +3903,24 @@ } }, "node_modules/ansi-regex": { - "version": "5.0.1", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", "dev": true, - "license": "MIT", "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" + "node": ">=12" }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "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": ">=8" + "node": ">=10" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" @@ -3266,8 +3928,9 @@ }, "node_modules/anymatch": { "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, - "license": "ISC", "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -3278,29 +3941,30 @@ }, "node_modules/app-root-path": { "version": "3.1.0", - "license": "MIT", + "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/applicationinsights": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/applicationinsights/-/applicationinsights-2.7.0.tgz", - "integrity": "sha512-/vV5X6M4TlRA5NxNZAdCE0gukzfK24mb3z18D5Kl/CyIfSVIkafsIji3mK+Zi5q+7dn6H1CkFazlcnLf40anHw==", + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/applicationinsights/-/applicationinsights-2.9.2.tgz", + "integrity": "sha512-wlDiD7v0BQNM8oNzsf9C836R5ze25u+CuCEZsbA5xMIXYYBxkqkWE/mo9GFJM7rsKaiGqpxEwWmePHKD2Lwy2w==", "dependencies": { - "@azure/core-auth": "^1.4.0", + "@azure/core-auth": "^1.5.0", "@azure/core-rest-pipeline": "1.10.1", "@azure/core-util": "1.2.0", - "@azure/opentelemetry-instrumentation-azure-sdk": "^1.0.0-beta.3", + "@azure/opentelemetry-instrumentation-azure-sdk": "^1.0.0-beta.5", "@microsoft/applicationinsights-web-snippet": "^1.0.1", - "@opentelemetry/api": "^1.0.4", - "@opentelemetry/core": "^1.12.0", - "@opentelemetry/sdk-trace-base": "^1.12.0", - "@opentelemetry/semantic-conventions": "^1.12.0", + "@opentelemetry/api": "^1.7.0", + "@opentelemetry/core": "^1.19.0", + "@opentelemetry/sdk-trace-base": "^1.19.0", + "@opentelemetry/semantic-conventions": "^1.19.0", "cls-hooked": "^4.2.2", "continuation-local-storage": "^3.2.1", - "diagnostic-channel": "1.1.0", - "diagnostic-channel-publishers": "1.0.6" + "diagnostic-channel": "1.1.1", + "diagnostic-channel-publishers": "1.0.8" }, "engines": { "node": ">=8.0.0" @@ -3314,51 +3978,88 @@ } } }, + "node_modules/applicationinsights/node_modules/@azure/core-rest-pipeline": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.10.1.tgz", + "integrity": "sha512-Kji9k6TOFRDB5ZMTw8qUf2IJ+CeJtsuMdAHox9eqpTf1cefiNMpzrfnF6sINEBZJsaVaWgQ0o48B6kcUH68niA==", + "dependencies": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-auth": "^1.4.0", + "@azure/core-tracing": "^1.0.1", + "@azure/core-util": "^1.0.0", + "@azure/logger": "^1.0.0", + "form-data": "^4.0.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "tslib": "^2.2.0", + "uuid": "^8.3.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/applicationinsights/node_modules/@azure/core-util": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.2.0.tgz", + "integrity": "sha512-ffGIw+Qs8bNKNLxz5UPkz4/VBM/EZY07mPve1ZYFqYUdPwFqRj0RPk0U7LZMOfT7GCck9YjuT1Rfp1PApNl1ng==", + "dependencies": { + "@azure/abort-controller": "^1.0.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/arg": { "version": "4.1.3", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true }, "node_modules/argparse": { "version": "2.0.1", - "license": "Python-2.0" + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, "node_modules/array-flatten": { "version": "1.1.1", - "license": "MIT" + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, "node_modules/array-timsort": { "version": "1.0.3", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/array-timsort/-/array-timsort-1.0.3.tgz", + "integrity": "sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==", + "dev": true }, "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, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/asap": { "version": "2.0.6", - "license": "MIT" + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" }, "node_modules/assert-never": { "version": "1.2.1", - "license": "MIT" + "resolved": "https://registry.npmjs.org/assert-never/-/assert-never-1.2.1.tgz", + "integrity": "sha512-TaTivMB6pYI1kXwrFlEhLeGfOqoDNdTxjCdwRfFFkEA30Eu+k48W34nlok2EYWJfFFzqaEmichdNM7th6M5HNw==" }, - "node_modules/astral-regex": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } + "node_modules/async": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==" }, "node_modules/async-hook-jl": { "version": "1.7.6", - "license": "MIT", + "resolved": "https://registry.npmjs.org/async-hook-jl/-/async-hook-jl-1.7.6.tgz", + "integrity": "sha512-gFaHkFfSxTjvoxDMYqDuGHlcRyUuamF8s+ZTtJdDzqjws4mCt7v0vuV79/E2Wr2/riMQgtG4/yUtXWs1gZ7JMg==", "dependencies": { "stack-chain": "^1.3.7" }, @@ -3368,7 +4069,8 @@ }, "node_modules/async-listener": { "version": "0.6.10", - "license": "BSD-2-Clause", + "resolved": "https://registry.npmjs.org/async-listener/-/async-listener-0.6.10.tgz", + "integrity": "sha512-gpuo6xOyF4D5DE5WvyqZdPA3NGhiT6Qf07l7DCB0wwDEsLvDIbCr6j9S5aj5Ch96dLace5tXVzWBZkxU/c5ohw==", "dependencies": { "semver": "^5.3.0", "shimmer": "^1.1.0" @@ -3378,15 +4080,17 @@ } }, "node_modules/async-listener/node_modules/semver": { - "version": "5.7.1", - "license": "ISC", + "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/async-prompt": { "version": "1.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/async-prompt/-/async-prompt-1.0.1.tgz", + "integrity": "sha512-AdeC4NCuD0RxpbWm6pexY/AWMasHzMe+/zvdxdsAvpMmuz4in7N8OrKP7YzbRPYhUS+hibZNdUablgfE6Uj8UA==", "dependencies": { "keypress": "~0.2.1" }, @@ -3396,11 +4100,13 @@ }, "node_modules/asynckit": { "version": "0.4.0", - "license": "MIT" + "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.5", - "license": "MIT", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", "engines": { "node": ">= 0.4" }, @@ -3409,23 +4115,25 @@ } }, "node_modules/axios": { - "version": "1.4.0", - "license": "MIT", + "version": "1.6.5", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.5.tgz", + "integrity": "sha512-Ii012v05KEVuUoFWmMW/UQv9aRIc3ZwkWDcM+h5Il8izZCtRVpDUfwpoFf7eOtajT3QiGR4yDUx7lPqHJULgbg==", "dependencies": { - "follow-redirects": "^1.15.0", + "follow-redirects": "^1.15.4", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } }, "node_modules/babel-jest": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", "dev": true, - "license": "MIT", "dependencies": { - "@jest/transform": "^29.5.0", + "@jest/transform": "^29.7.0", "@types/babel__core": "^7.1.14", "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.5.0", + "babel-preset-jest": "^29.6.3", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "slash": "^3.0.0" @@ -3437,10 +4145,60 @@ "@babel/core": "^7.8.0" } }, + "node_modules/babel-jest/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/babel-jest/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/babel-jest/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/babel-jest/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/babel-plugin-istanbul": { "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", "@istanbuljs/load-nyc-config": "^1.0.0", @@ -3452,10 +4210,36 @@ "node": ">=8" } }, + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/babel-plugin-jest-hoist": { - "version": "29.5.0", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", "dev": true, - "license": "MIT", "dependencies": { "@babel/template": "^7.3.3", "@babel/types": "^7.3.3", @@ -3468,8 +4252,9 @@ }, "node_modules/babel-preset-current-node-syntax": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", "dev": true, - "license": "MIT", "dependencies": { "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-bigint": "^7.8.3", @@ -3489,11 +4274,12 @@ } }, "node_modules/babel-preset-jest": { - "version": "29.5.0", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", "dev": true, - "license": "MIT", "dependencies": { - "babel-plugin-jest-hoist": "^29.5.0", + "babel-plugin-jest-hoist": "^29.6.3", "babel-preset-current-node-syntax": "^1.0.0" }, "engines": { @@ -3505,7 +4291,8 @@ }, "node_modules/babel-walk": { "version": "3.0.0-canary-5", - "license": "MIT", + "resolved": "https://registry.npmjs.org/babel-walk/-/babel-walk-3.0.0-canary-5.tgz", + "integrity": "sha512-GAwkz0AihzY5bkwIY5QDR+LvsRQgB/B+1foMPvi0FZPMl5fjD7ICiznUiBdLYMH1QYe6vqu4gWYytZOccLouFw==", "dependencies": { "@babel/types": "^7.9.6" }, @@ -3515,10 +4302,13 @@ }, "node_modules/balanced-match": { "version": "1.0.2", - "license": "MIT" + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "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", @@ -3532,19 +4322,20 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "license": "MIT" + ] }, "node_modules/base64url": { "version": "3.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz", + "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==", "engines": { "node": ">=6.0.0" } }, "node_modules/basic-auth": { "version": "2.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", "dependencies": { "safe-buffer": "5.1.2" }, @@ -3553,12 +4344,14 @@ } }, "node_modules/before-after-hook": { - "version": "2.2.2", - "license": "Apache-2.0" + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", + "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==" }, "node_modules/body-parser": { "version": "1.20.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", + "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", "dependencies": { "bytes": "3.1.2", "content-type": "~1.0.5", @@ -3580,37 +4373,31 @@ }, "node_modules/body-parser/node_modules/debug": { "version": "2.6.9", - "license": "MIT", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dependencies": { "ms": "2.0.0" } }, "node_modules/body-parser/node_modules/ms": { "version": "2.0.0", - "license": "MIT" - }, - "node_modules/body-parser/node_modules/on-finished": { - "version": "2.4.1", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "node_modules/brace-expansion": { - "version": "1.1.11", - "license": "MIT", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "balanced-match": "^1.0.0" } }, "node_modules/braces": { "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, - "license": "MIT", "dependencies": { "fill-range": "^7.0.1" }, @@ -3619,7 +4406,9 @@ } }, "node_modules/browserslist": { - "version": "4.21.5", + "version": "4.22.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.2.tgz", + "integrity": "sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==", "dev": true, "funding": [ { @@ -3629,14 +4418,17 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001449", - "electron-to-chromium": "^1.4.284", - "node-releases": "^2.0.8", - "update-browserslist-db": "^1.0.10" + "caniuse-lite": "^1.0.30001565", + "electron-to-chromium": "^1.4.601", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" }, "bin": { "browserslist": "cli.js" @@ -3647,8 +4439,9 @@ }, "node_modules/bs-logger": { "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", "dev": true, - "license": "MIT", "dependencies": { "fast-json-stable-stringify": "2.x" }, @@ -3658,18 +4451,22 @@ }, "node_modules/bser": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", "dev": true, - "license": "Apache-2.0", "dependencies": { "node-int64": "^0.4.0" } }, "node_modules/btoa-lite": { "version": "1.0.0", - "license": "MIT" + "resolved": "https://registry.npmjs.org/btoa-lite/-/btoa-lite-1.0.0.tgz", + "integrity": "sha512-gvW7InbIyF8AicrqWoptdW08pUxuhq8BEgowNajy9RhiE86fmGAGl+bLKo6oB8QP0CkqHLowfN0oJdKC/J6LbA==" }, "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", @@ -3684,7 +4481,6 @@ "url": "https://feross.org/support" } ], - "license": "MIT", "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" @@ -3692,34 +4488,51 @@ }, "node_modules/buffer-equal-constant-time": { "version": "1.0.1", - "license": "BSD-3-Clause" + "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", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true }, "node_modules/buffer-writer": { "version": "2.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", + "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==", "engines": { "node": ">=4" } }, + "node_modules/builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/builtins": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", + "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==", "dev": true, - "license": "MIT", "dependencies": { "semver": "^7.0.0" } }, "node_modules/bunyan": { "version": "1.8.15", + "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.15.tgz", + "integrity": "sha512-0tECWShh6wUysgucJcBAoYegf3JJoZWibxdqhTm7OHPeT42qdjkZ29QCMcKwbgU1kiH+auSIasNRXMLWXafXig==", "engines": [ "node >=0.10.0" ], - "license": "MIT", "bin": { "bunyan": "bin/bunyan" }, @@ -3732,14 +4545,16 @@ }, "node_modules/bytes": { "version": "3.1.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "engines": { "node": ">= 0.8" } }, "node_modules/cache-manager": { "version": "3.6.3", - "license": "MIT", + "resolved": "https://registry.npmjs.org/cache-manager/-/cache-manager-3.6.3.tgz", + "integrity": "sha512-dS4DnV6c6cQcVH5OxzIU1XZaACXwvVIiUPkFytnRmLOACuBGv3GQgRQ1RJGRRw4/9DF14ZK2RFlZu1TUgDniMg==", "dependencies": { "async": "3.2.3", "lodash.clonedeep": "^4.5.0", @@ -3748,14 +4563,28 @@ }, "node_modules/cache-manager/node_modules/async": { "version": "3.2.3", - "license": "MIT" + "resolved": "https://registry.npmjs.org/async/-/async-3.2.3.tgz", + "integrity": "sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==" + }, + "node_modules/cache-manager/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } }, "node_modules/call-bind": { - "version": "1.0.2", - "license": "MIT", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", + "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.1", + "set-function-length": "^1.1.1" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3763,22 +4592,26 @@ }, "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, - "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/camelcase": { "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/caniuse-lite": { - "version": "1.0.30001486", + "version": "1.0.30001579", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001579.tgz", + "integrity": "sha512-u5AUVkixruKHJjw/pj9wISlcMpgFWzSrczLZbrqBSxukQixmg0SJ5sZTpvaFvxU0HoQKd4yoyAogyrAz9pzJnA==", "dev": true, "funding": [ { @@ -3793,41 +4626,56 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ], - "license": "CC-BY-4.0" + ] }, "node_modules/chalk": { - "version": "4.1.2", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk-template": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/chalk-template/-/chalk-template-1.1.0.tgz", + "integrity": "sha512-T2VJbcDuZQ0Tb2EWwSotMPJjgpy1/tGee1BTpUNsGZ/qgNjV2t7Mvu+d4600U564nbLesN1x2dPL+xii174Ekg==", "dev": true, - "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "chalk": "^5.2.0" }, "engines": { - "node": ">=10" + "node": ">=14.16" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/chalk/chalk-template?sponsor=1" } }, "node_modules/char-regex": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" } }, "node_modules/character-parser": { "version": "2.2.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz", + "integrity": "sha512-+UqJQjFEFaTAs3bNsF2j2kEN1baG/zghZbdqoYEDxGZtJo9LBzl1A+m0D4n3qKx8N2FNv8/Xp6yV9mQmBuptaw==", "dependencies": { "is-regex": "^1.0.3" } }, "node_modules/ci-info": { - "version": "3.8.0", + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", "dev": true, "funding": [ { @@ -3835,28 +4683,20 @@ "url": "https://github.com/sponsors/sibiraj-s" } ], - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/cjs-module-lexer": { - "version": "1.2.2", - "dev": true, - "license": "MIT" - }, - "node_modules/clean-stack": { - "version": "2.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", + "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==" }, "node_modules/clear-module": { "version": "4.1.2", + "resolved": "https://registry.npmjs.org/clear-module/-/clear-module-4.1.2.tgz", + "integrity": "sha512-LWAxzHqdHsAZlPlEyJ2Poz6AIs384mPeqLVCru2p0BrP9G/kVGuhNyZYClLO6cXlnuJjzC8xtsJIuMjKqLXoAw==", "dev": true, - "license": "MIT", "dependencies": { "parent-module": "^2.0.0", "resolve-from": "^5.0.0" @@ -3869,93 +4709,154 @@ } }, "node_modules/cli-cursor": { - "version": "3.1.0", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", + "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", "dev": true, - "license": "MIT", "dependencies": { - "restore-cursor": "^3.1.0" + "restore-cursor": "^4.0.0" }, "engines": { - "node": ">=8" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/cli-truncate": { - "version": "3.1.0", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", + "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", "dev": true, - "license": "MIT", "dependencies": { "slice-ansi": "^5.0.0", - "string-width": "^5.0.0" + "string-width": "^7.0.0" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/cli-truncate/node_modules/ansi-regex": { - "version": "6.0.1", + "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==", "dev": true, - "license": "MIT", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, "engines": { "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/cli-truncate/node_modules/emoji-regex": { - "version": "9.2.2", + "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==", "dev": true, - "license": "MIT" + "engines": { + "node": ">=8" + } }, - "node_modules/cli-truncate/node_modules/string-width": { - "version": "5.1.2", + "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==", "dev": true, - "license": "MIT", "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" + "color-convert": "^2.0.1" }, "engines": { - "node": ">=12" + "node": ">=8" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/cli-truncate/node_modules/strip-ansi": { - "version": "7.0.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==", "dev": true, - "license": "MIT", "dependencies": { - "ansi-regex": "^6.0.1" + "color-name": "~1.1.4" }, "engines": { - "node": ">=12" + "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==", + "dev": true + }, + "node_modules/cliui/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/cliui/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/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==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "engines": { + "node": ">=8" } }, - "node_modules/cliui": { - "version": "8.0.1", + "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==", "dev": true, - "license": "ISC", "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" + "ansi-regex": "^5.0.1" }, "engines": { - "node": ">=12" + "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==", + "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/cls-hooked": { "version": "4.2.2", - "license": "BSD-2-Clause", + "resolved": "https://registry.npmjs.org/cls-hooked/-/cls-hooked-4.2.2.tgz", + "integrity": "sha512-J4Xj5f5wq/4jAvcdgoGsL3G103BtWpZrMo8NEinRltN+xpTZdI+M38pyQqhuFU/P792xkMFvnKSf+Lm81U1bxw==", "dependencies": { "async-hook-jl": "^1.7.6", "emitter-listener": "^1.0.1", @@ -3966,23 +4867,26 @@ } }, "node_modules/cls-hooked/node_modules/semver": { - "version": "5.7.1", - "license": "ISC", + "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/cluster-key-slot": { "version": "1.1.2", - "license": "Apache-2.0", + "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/co": { "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", "dev": true, - "license": "MIT", "engines": { "iojs": ">= 1.0.0", "node": ">= 0.12.0" @@ -3990,42 +4894,46 @@ }, "node_modules/code-block-writer": { "version": "11.0.3", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-11.0.3.tgz", + "integrity": "sha512-NiujjUFB4SwScJq2bwbYUtXbZhBSlY6vYzm++3Q6oC+U+injTqfPYFK8wS9COOmb2lueqp0ZRB4nK1VYeHgNyw==", + "dev": true }, "node_modules/collect-v8-coverage": { - "version": "1.0.1", - "dev": true, - "license": "MIT" + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", + "dev": true }, "node_modules/color-contrast-checker": { "version": "2.1.0", - "license": "Apache-2.0" + "resolved": "https://registry.npmjs.org/color-contrast-checker/-/color-contrast-checker-2.1.0.tgz", + "integrity": "sha512-6Y0aIEej3pwZTVlicIqVzhO6T4izDWouaIXnYoDdTuFFAMQ9nnN0dgHNP9J94jRnH6asjPq1/wzUKxwoNbWtRQ==" }, "node_modules/color-convert": { - "version": "2.0.1", + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, - "license": "MIT", "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" + "color-name": "1.1.3" } }, "node_modules/color-name": { - "version": "1.1.4", - "dev": true, - "license": "MIT" + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true }, "node_modules/colorette": { "version": "2.0.20", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true }, "node_modules/combined-stream": { "version": "1.0.8", - "license": "MIT", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dependencies": { "delayed-stream": "~1.0.0" }, @@ -4034,17 +4942,19 @@ } }, "node_modules/commander": { - "version": "10.0.1", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", "dev": true, - "license": "MIT", "engines": { - "node": ">=14" + "node": ">=16" } }, "node_modules/comment-json": { "version": "4.2.3", + "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.2.3.tgz", + "integrity": "sha512-SsxdiOf064DWoZLH799Ata6u7iV658A11PlWtZATDlXPpKGJnbJZ5Z24ybixAi+LUUqJ/GKowAejtC5GFUG7Tw==", "dev": true, - "license": "MIT", "dependencies": { "array-timsort": "^1.0.3", "core-util-is": "^1.0.3", @@ -4058,7 +4968,8 @@ }, "node_modules/compressible": { "version": "2.0.18", - "license": "MIT", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", "dependencies": { "mime-db": ">= 1.43.0 < 2" }, @@ -4068,7 +4979,8 @@ }, "node_modules/compression": { "version": "1.7.4", - "license": "MIT", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", "dependencies": { "accepts": "~1.3.5", "bytes": "3.0.0", @@ -4084,56 +4996,53 @@ }, "node_modules/compression/node_modules/bytes": { "version": "3.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", "engines": { "node": ">= 0.8" } }, "node_modules/compression/node_modules/debug": { "version": "2.6.9", - "license": "MIT", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dependencies": { "ms": "2.0.0" } }, "node_modules/compression/node_modules/ms": { "version": "2.0.0", - "license": "MIT" + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "node_modules/concat-map": { "version": "0.0.1", - "license": "MIT" + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "node_modules/configstore": { - "version": "5.0.1", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-6.0.0.tgz", + "integrity": "sha512-cD31W1v3GqUlQvbBCGcXmd2Nj9SvLDOP1oQ0YFuLETufzSPaKp11rYBsSOm7rCsW3OnIRAFM3OxRhceaXNYHkA==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { - "dot-prop": "^5.2.0", - "graceful-fs": "^4.1.2", - "make-dir": "^3.0.0", - "unique-string": "^2.0.0", - "write-file-atomic": "^3.0.0", - "xdg-basedir": "^4.0.0" + "dot-prop": "^6.0.1", + "graceful-fs": "^4.2.6", + "unique-string": "^3.0.0", + "write-file-atomic": "^3.0.3", + "xdg-basedir": "^5.0.1" }, "engines": { - "node": ">=8" - } - }, - "node_modules/configstore/node_modules/write-file-atomic": { - "version": "3.0.3", - "dev": true, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/yeoman/configstore?sponsor=1" } }, "node_modules/connect-redis": { "version": "7.1.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/connect-redis/-/connect-redis-7.1.0.tgz", + "integrity": "sha512-UaqO1EirWjON2ENsyau7N5lbkrdYBpS6mYlXSeff/OYXsd6EGZ+SXSmNPoljL2PSua8fgjAEaldSA73PMZQ9Eg==", "engines": { "node": ">=16" }, @@ -4143,7 +5052,8 @@ }, "node_modules/constantinople": { "version": "4.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-4.0.1.tgz", + "integrity": "sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw==", "dependencies": { "@babel/parser": "^7.6.0", "@babel/types": "^7.6.1" @@ -4151,7 +5061,8 @@ }, "node_modules/content-disposition": { "version": "0.5.4", - "license": "MIT", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", "dependencies": { "safe-buffer": "5.2.1" }, @@ -4161,6 +5072,8 @@ }, "node_modules/content-disposition/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", @@ -4174,19 +5087,20 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "license": "MIT" + ] }, "node_modules/content-type": { "version": "1.0.5", - "license": "MIT", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "engines": { "node": ">= 0.6" } }, "node_modules/continuation-local-storage": { "version": "3.2.1", - "license": "BSD-2-Clause", + "resolved": "https://registry.npmjs.org/continuation-local-storage/-/continuation-local-storage-3.2.1.tgz", + "integrity": "sha512-jx44cconVqkCEEyLSKWwkvUXwO561jXMa3LPjTPsm5QR22PA0/mhe33FT4Xb5y74JDvt/Cq+5lm8S8rskLv9ZA==", "dependencies": { "async-listener": "^0.6.0", "emitter-listener": "^1.1.1" @@ -4194,28 +5108,33 @@ }, "node_modules/convert-source-map": { "version": "2.0.0", - "dev": true, - "license": "MIT" + "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/cookie": { "version": "0.5.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", "engines": { "node": ">= 0.6" } }, "node_modules/cookie-signature": { "version": "1.0.6", - "license": "MIT" + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, "node_modules/core-util-is": { "version": "1.0.3", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true }, "node_modules/cors": { "version": "2.8.5", - "license": "MIT", + "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" @@ -4225,180 +5144,319 @@ } }, "node_modules/cosmiconfig": { - "version": "8.0.0", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", "dev": true, - "license": "MIT", "dependencies": { + "@types/parse-json": "^4.0.0", "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", "parse-json": "^5.0.0", - "path-type": "^4.0.0" + "path-type": "^4.0.0", + "yaml": "^1.10.0" }, "engines": { - "node": ">=14" + "node": ">=10" + } + }, + "node_modules/cosmiconfig/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/create-jest/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/create-jest/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/create-jest/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/create-jest/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/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==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, - "license": "MIT" + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } }, "node_modules/crypto-random-string": { - "version": "2.0.0", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-4.0.0.tgz", + "integrity": "sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==", "dev": true, - "license": "MIT", + "dependencies": { + "type-fest": "^1.0.1" + }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/cspell": { - "version": "6.31.1", + "node_modules/crypto-random-string/node_modules/type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", "dev": true, - "license": "MIT", - "dependencies": { - "@cspell/cspell-pipe": "6.31.1", - "@cspell/dynamic-import": "6.31.1", - "chalk": "^4.1.2", - "commander": "^10.0.0", - "cspell-gitignore": "6.31.1", - "cspell-glob": "6.31.1", - "cspell-io": "6.31.1", - "cspell-lib": "6.31.1", - "fast-glob": "^3.2.12", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cspell": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/cspell/-/cspell-8.3.2.tgz", + "integrity": "sha512-V8Ub3RO/a5lwSsltW/ib3Z3G/sczKtSpBBN1JChzbSCfEgaY2mJY8JW0BpkSV+Ug6uJitpXNOOaxa3Xr489i7g==", + "dev": true, + "dependencies": { + "@cspell/cspell-json-reporter": "8.3.2", + "@cspell/cspell-pipe": "8.3.2", + "@cspell/cspell-types": "8.3.2", + "@cspell/dynamic-import": "8.3.2", + "chalk": "^5.3.0", + "chalk-template": "^1.1.0", + "commander": "^11.1.0", + "cspell-gitignore": "8.3.2", + "cspell-glob": "8.3.2", + "cspell-io": "8.3.2", + "cspell-lib": "8.3.2", + "fast-glob": "^3.3.2", "fast-json-stable-stringify": "^2.1.0", - "file-entry-cache": "^6.0.1", - "get-stdin": "^8.0.0", - "imurmurhash": "^0.1.4", - "semver": "^7.3.8", - "strip-ansi": "^6.0.1", - "vscode-uri": "^3.0.7" + "file-entry-cache": "^8.0.0", + "get-stdin": "^9.0.0", + "semver": "^7.5.4", + "strip-ansi": "^7.1.0", + "vscode-uri": "^3.0.8" }, "bin": { - "cspell": "bin.js", + "cspell": "bin.mjs", "cspell-esm": "bin.mjs" }, "engines": { - "node": ">=14" + "node": ">=18" }, "funding": { "url": "https://github.com/streetsidesoftware/cspell?sponsor=1" } }, + "node_modules/cspell-config-lib": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/cspell-config-lib/-/cspell-config-lib-8.3.2.tgz", + "integrity": "sha512-Wc98XhBNLwDxnxCzMtgRJALI9a69cu3C5Gf1rGjNTKSFo9JYiQmju0Ur3z25Pkx9Sa86f+2IjvNCf33rUDSoBQ==", + "dev": true, + "dependencies": { + "@cspell/cspell-types": "8.3.2", + "comment-json": "^4.2.3", + "yaml": "^2.3.4" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/cspell-dictionary": { - "version": "6.31.1", + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/cspell-dictionary/-/cspell-dictionary-8.3.2.tgz", + "integrity": "sha512-xyK95hO2BMPFxIo8zBwGml8035qOxSBdga1BMhwW/p2wDrQP8S4Cdm/54//tCDmKn6uRkFQvyOfWGaX2l8WMEg==", "dev": true, - "license": "MIT", "dependencies": { - "@cspell/cspell-pipe": "6.31.1", - "@cspell/cspell-types": "6.31.1", - "cspell-trie-lib": "6.31.1", - "fast-equals": "^4.0.3", - "gensequence": "^5.0.2" + "@cspell/cspell-pipe": "8.3.2", + "@cspell/cspell-types": "8.3.2", + "cspell-trie-lib": "8.3.2", + "fast-equals": "^5.0.1", + "gensequence": "^6.0.0" }, "engines": { - "node": ">=14" + "node": ">=18" } }, "node_modules/cspell-gitignore": { - "version": "6.31.1", + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/cspell-gitignore/-/cspell-gitignore-8.3.2.tgz", + "integrity": "sha512-3Qc9P5BVvl/cg//s2s+zIMGKcoH5v7oOtRgwn4UQry8yiyo19h0tiTKkSR574FMhF5NtcShTnwIwPSIXVBPFHA==", "dev": true, - "license": "MIT", "dependencies": { - "cspell-glob": "6.31.1", - "find-up": "^5.0.0" + "cspell-glob": "8.3.2", + "find-up-simple": "^1.0.0" }, "bin": { "cspell-gitignore": "bin.mjs" }, "engines": { - "node": ">=14" + "node": ">=18" } }, "node_modules/cspell-glob": { - "version": "6.31.1", + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/cspell-glob/-/cspell-glob-8.3.2.tgz", + "integrity": "sha512-KtIFxE+3l5dGEofND4/CdZffXP8XN1+XGQKxJ96lIzWsc01mkotfhxTkla6mgvfH039t7BsY/SWv0460KyGslQ==", "dev": true, - "license": "MIT", "dependencies": { "micromatch": "^4.0.5" }, "engines": { - "node": ">=14" + "node": ">=18" } }, "node_modules/cspell-grammar": { - "version": "6.31.1", + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/cspell-grammar/-/cspell-grammar-8.3.2.tgz", + "integrity": "sha512-tYCkOmRzJe1a6/R+8QGSwG7TwTgznLPqsHtepKzLmnS4YX54VXjKRI9zMARxXDzUVfyCSVdW5MyiY/0WTNoy+A==", "dev": true, - "license": "MIT", "dependencies": { - "@cspell/cspell-pipe": "6.31.1", - "@cspell/cspell-types": "6.31.1" + "@cspell/cspell-pipe": "8.3.2", + "@cspell/cspell-types": "8.3.2" }, "bin": { "cspell-grammar": "bin.mjs" }, "engines": { - "node": ">=14" + "node": ">=18" } }, "node_modules/cspell-io": { - "version": "6.31.1", + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/cspell-io/-/cspell-io-8.3.2.tgz", + "integrity": "sha512-WYpKsyBCQP0SY4gXnhW5fPuxcYchKYKG1PIXVV3ezFU4muSgW6GuLNbGuSfwv/8YNXRgFSN0e3hYH0rdBK2Aow==", "dev": true, - "license": "MIT", "dependencies": { - "@cspell/cspell-service-bus": "6.31.1", - "node-fetch": "^2.6.9" + "@cspell/cspell-service-bus": "8.3.2" }, "engines": { - "node": ">=14" + "node": ">=18" } }, "node_modules/cspell-lib": { - "version": "6.31.1", + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/cspell-lib/-/cspell-lib-8.3.2.tgz", + "integrity": "sha512-wTvdaev/TyGB/ln6CVD1QbVs2D7/+QiajQ67S7yj1suLHM6YcNQQb/5sPAM8VPtj0E7PgwgPXf3bq18OtPvnFg==", "dev": true, - "license": "MIT", "dependencies": { - "@cspell/cspell-bundled-dicts": "6.31.1", - "@cspell/cspell-pipe": "6.31.1", - "@cspell/cspell-types": "6.31.1", - "@cspell/strong-weak-map": "6.31.1", + "@cspell/cspell-bundled-dicts": "8.3.2", + "@cspell/cspell-pipe": "8.3.2", + "@cspell/cspell-resolver": "8.3.2", + "@cspell/cspell-types": "8.3.2", + "@cspell/dynamic-import": "8.3.2", + "@cspell/strong-weak-map": "8.3.2", "clear-module": "^4.1.2", "comment-json": "^4.2.3", - "configstore": "^5.0.1", - "cosmiconfig": "8.0.0", - "cspell-dictionary": "6.31.1", - "cspell-glob": "6.31.1", - "cspell-grammar": "6.31.1", - "cspell-io": "6.31.1", - "cspell-trie-lib": "6.31.1", - "fast-equals": "^4.0.3", - "find-up": "^5.0.0", - "gensequence": "^5.0.2", + "configstore": "^6.0.0", + "cspell-config-lib": "8.3.2", + "cspell-dictionary": "8.3.2", + "cspell-glob": "8.3.2", + "cspell-grammar": "8.3.2", + "cspell-io": "8.3.2", + "cspell-trie-lib": "8.3.2", + "fast-equals": "^5.0.1", + "gensequence": "^6.0.0", "import-fresh": "^3.3.0", "resolve-from": "^5.0.0", - "resolve-global": "^1.0.0", - "vscode-languageserver-textdocument": "^1.0.8", - "vscode-uri": "^3.0.7" + "vscode-languageserver-textdocument": "^1.0.11", + "vscode-uri": "^3.0.8" }, "engines": { - "node": ">=14.6" + "node": ">=18" } }, "node_modules/cspell-trie-lib": { - "version": "6.31.1", + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/cspell-trie-lib/-/cspell-trie-lib-8.3.2.tgz", + "integrity": "sha512-8qh2FqzkLMwzlTlvO/5Z+89fhi30rrfekocpight/BmqKbE2XFJQD7wS2ml24e7q/rdHJLXVpJbY/V5mByucCA==", "dev": true, - "license": "MIT", "dependencies": { - "@cspell/cspell-pipe": "6.31.1", - "@cspell/cspell-types": "6.31.1", - "gensequence": "^5.0.2" + "@cspell/cspell-pipe": "8.3.2", + "@cspell/cspell-types": "8.3.2", + "gensequence": "^6.0.0" }, "engines": { - "node": ">=14" + "node": ">=18" } }, "node_modules/debug": { "version": "4.3.4", - "license": "MIT", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dependencies": { "ms": "2.1.2" }, @@ -4412,57 +5470,87 @@ } }, "node_modules/dedent": { - "version": "0.7.0", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", + "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==", "dev": true, - "license": "MIT" + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } }, "node_modules/deeks": { - "version": "2.6.0", - "license": "MIT", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/deeks/-/deeks-2.6.1.tgz", + "integrity": "sha512-PZrpz5xLo2JPZa3L+kqMMMdZU5pRwMysTM1xd6pLhNtgQw4Iq3wbF2QWaQTVh+HRq9Yg4rcjDIJ+scfGLxmsjQ==", "engines": { "node": ">= 12" } }, "node_modules/deep-is": { "version": "0.1.4", - "dev": true, - "license": "MIT" + "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", - "license": "MIT", + "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/define-lazy-prop": { + "node_modules/define-data-property": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", + "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", + "dependencies": { + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/define-lazy-prop": { "version": "2.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", "engines": { "node": ">=8" } }, "node_modules/delayed-stream": { "version": "1.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "engines": { "node": ">=0.4.0" } }, "node_modules/depd": { "version": "2.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "engines": { "node": ">= 0.8" } }, "node_modules/deprecation": { "version": "2.3.1", - "license": "ISC" + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" }, "node_modules/destroy": { "version": "1.2.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", "engines": { "node": ">= 0.8", "npm": "1.2.8000 || >= 1.4.16" @@ -4470,53 +5558,52 @@ }, "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==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/diagnostic-channel": { - "version": "1.1.0", - "license": "MIT", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/diagnostic-channel/-/diagnostic-channel-1.1.1.tgz", + "integrity": "sha512-r2HV5qFkUICyoaKlBEpLKHjxMXATUf/l+h8UZPGBHGLy4DDiY2sOLcIctax4eRnTw5wH2jTMExLntGPJ8eOJxw==", "dependencies": { - "semver": "^5.3.0" + "semver": "^7.5.3" } }, "node_modules/diagnostic-channel-publishers": { - "version": "1.0.6", - "license": "MIT", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/diagnostic-channel-publishers/-/diagnostic-channel-publishers-1.0.8.tgz", + "integrity": "sha512-HmSm9hXxSPxA9BaLGY98QU1zsdjeCk113KjAYGPCen1ZP6mhVaTPzHd6UYv5r21DnWANi+f+NyPOHruGT9jpqQ==", "peerDependencies": { "diagnostic-channel": "*" } }, - "node_modules/diagnostic-channel/node_modules/semver": { - "version": "5.7.1", - "license": "ISC", - "bin": { - "semver": "bin/semver" - } - }, "node_modules/diff": { "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", "dev": true, - "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" } }, "node_modules/diff-sequences": { - "version": "29.4.3", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", "dev": true, - "license": "MIT", "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "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, - "license": "MIT", "dependencies": { "path-type": "^4.0.0" }, @@ -4525,16 +5612,18 @@ } }, "node_modules/doc-path": { - "version": "3.0.6", - "license": "MIT", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/doc-path/-/doc-path-3.1.0.tgz", + "integrity": "sha512-Pv2hLQbUM8du5681lTWIYk0OtVBmNhMAeZNGeFhMMJBIR89Nw4XesBwee1Xtlfk83n71tn0Y6VsJOn4d3qIiTw==", "engines": { "node": ">=12" } }, "node_modules/doctrine": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, - "license": "Apache-2.0", "dependencies": { "esutils": "^2.0.2" }, @@ -4544,17 +5633,22 @@ }, "node_modules/doctypes": { "version": "1.1.0", - "license": "MIT" + "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz", + "integrity": "sha512-LLBi6pEqS6Do3EKQ3J0NqHWV5hhb78Pi8vvESYwyOy2c31ZEZVdtitdzsQsKb7878PEERhzUk0ftqGhG6Mz+pQ==" }, "node_modules/dot-prop": { - "version": "5.3.0", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz", + "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==", "dev": true, - "license": "MIT", "dependencies": { "is-obj": "^2.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/dotenv": { @@ -4570,8 +5664,9 @@ }, "node_modules/dtrace-provider": { "version": "0.8.8", + "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.8.tgz", + "integrity": "sha512-b7Z7cNtHPhH9EJhNNbbeqTcXB8LGFFZhq1PGgEvpeHlzd36bhbdTWoE/Ba/YguqpBSlAPKnARWhVlhunCMwfxg==", "hasInstallScript": true, - "license": "BSD-2-Clause", "optional": true, "dependencies": { "nan": "^2.14.0" @@ -4582,36 +5677,42 @@ }, "node_modules/eastasianwidth": { "version": "0.2.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true }, "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", - "license": "Apache-2.0", + "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/ee-first": { "version": "1.1.1", - "license": "MIT" + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/electron-to-chromium": { - "version": "1.4.392", - "dev": true, - "license": "ISC" + "version": "1.4.639", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.639.tgz", + "integrity": "sha512-CkKf3ZUVZchr+zDpAlNLEEy2NJJ9T64ULWaDgy3THXXlPVPkLu3VOs9Bac44nebVtdwl2geSj6AxTtGDOxoXhg==", + "dev": true }, "node_modules/emitter-listener": { "version": "1.1.2", - "license": "BSD-2-Clause", + "resolved": "https://registry.npmjs.org/emitter-listener/-/emitter-listener-1.1.2.tgz", + "integrity": "sha512-Bt1sBAGFHY9DKY+4/2cV6izcKJUf5T7/gkdmkxzX/qv9CcGH8xSwVRW5mtX03SWJtRTWSOpzCuWN9rBFYZepZQ==", "dependencies": { "shimmer": "^1.2.0" } }, "node_modules/emittery": { "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=12" }, @@ -4620,44 +5721,63 @@ } }, "node_modules/emoji-regex": { - "version": "8.0.0", - "dev": true, - "license": "MIT" + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", + "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==", + "dev": true }, "node_modules/encodeurl": { "version": "1.0.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", "engines": { "node": ">= 0.8" } }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/error-ex": { "version": "1.3.2", - "license": "MIT", + "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/es6-promise": { "version": "4.2.8", - "license": "MIT" + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" }, "node_modules/escalade": { "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/escape-html": { "version": "1.0.3", - "license": "MIT" + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" }, "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, - "license": "MIT", "engines": { "node": ">=10" }, @@ -4666,26 +5786,28 @@ } }, "node_modules/eslint": { - "version": "8.41.0", + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", + "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", "dev": true, - "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.0.3", - "@eslint/js": "8.41.0", - "@humanwhocodes/config-array": "^0.11.8", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.56.0", + "@humanwhocodes/config-array": "^0.11.13", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.0", - "eslint-visitor-keys": "^3.4.1", - "espree": "^9.5.2", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -4695,7 +5817,6 @@ "globals": "^13.19.0", "graphemer": "^1.4.0", "ignore": "^5.2.0", - "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", @@ -4705,9 +5826,8 @@ "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.1", + "optionator": "^0.9.3", "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" }, "bin": { @@ -4720,10 +5840,23 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint-compat-utils": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.1.2.tgz", + "integrity": "sha512-Jia4JDldWnFNIru1Ehx1H5s9/yxiRHY/TimCuUc0jNexew3cF1gI6CYZil1ociakfWO3rRqFjl1mskBblB3RYg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "eslint": ">=6.0.0" + } + }, "node_modules/eslint-config-prettier": { - "version": "8.8.0", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", "dev": true, - "license": "MIT", "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -4732,12 +5865,14 @@ } }, "node_modules/eslint-plugin-es-x": { - "version": "6.2.1", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-es-x/-/eslint-plugin-es-x-7.5.0.tgz", + "integrity": "sha512-ODswlDSO0HJDzXU0XvgZ3lF3lS3XAZEossh15Q2UHjwrJggWeBoKqqEsLTZLXl+dh5eOAozG0zRcYtuE35oTuQ==", "dev": true, - "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.1.2", - "@eslint-community/regexpp": "^4.5.0" + "@eslint-community/regexpp": "^4.6.0", + "eslint-compat-utils": "^0.1.2" }, "engines": { "node": "^14.18.0 || >=16.0.0" @@ -4750,18 +5885,22 @@ } }, "node_modules/eslint-plugin-n": { - "version": "16.0.0", + "version": "16.6.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-16.6.2.tgz", + "integrity": "sha512-6TyDmZ1HXoFQXnhCTUjVFULReoBPOAjpuiKELMkeP40yffI/1ZRO+d9ug/VC6fqISo2WkuIBk3cvuRPALaWlOQ==", "dev": true, - "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "builtins": "^5.0.1", - "eslint-plugin-es-x": "^6.1.0", - "ignore": "^5.1.1", - "is-core-module": "^2.12.0", + "eslint-plugin-es-x": "^7.5.0", + "get-tsconfig": "^4.7.0", + "globals": "^13.24.0", + "ignore": "^5.2.4", + "is-builtin-module": "^3.2.1", + "is-core-module": "^2.12.1", "minimatch": "^3.1.2", "resolve": "^1.22.2", - "semver": "^7.5.0" + "semver": "^7.5.3" }, "engines": { "node": ">=16.0.0" @@ -4773,42 +5912,79 @@ "eslint": ">=7.0.0" } }, + "node_modules/eslint-plugin-n/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint-plugin-n/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-prettier": { - "version": "4.2.1", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.3.tgz", + "integrity": "sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw==", "dev": true, - "license": "MIT", "dependencies": { - "prettier-linter-helpers": "^1.0.0" + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.8.6" }, "engines": { - "node": ">=12.0.0" + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" }, "peerDependencies": { - "eslint": ">=7.28.0", - "prettier": ">=2.0.0" + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": "*", + "prettier": ">=3.0.0" }, "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, "eslint-config-prettier": { "optional": true } } }, "node_modules/eslint-scope": { - "version": "5.1.1", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" + "estraverse": "^5.2.0" }, "engines": { - "node": ">=8.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/eslint-visitor-keys": { - "version": "3.4.1", + "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, - "license": "Apache-2.0", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -4816,48 +5992,146 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/cross-spawn": { - "version": "7.0.3", + "node_modules/eslint/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/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, - "license": "MIT", "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" + "color-convert": "^2.0.1" }, "engines": { - "node": ">= 8" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.2.0", + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" + "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": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=10" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/eslint/node_modules/estraverse": { - "version": "5.3.0", + "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/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "dev": true, - "license": "BSD-2-Clause", + "dependencies": { + "flat-cache": "^3.0.4" + }, "engines": { - "node": ">=4.0" + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/eslint/node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "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/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/eslint/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/espree": { - "version": "9.5.2", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { - "acorn": "^8.8.0", + "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^3.4.1" }, @@ -4870,8 +6144,9 @@ }, "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, - "license": "BSD-2-Clause", "bin": { "esparse": "bin/esparse.js", "esvalidate": "bin/esvalidate.js" @@ -4882,8 +6157,9 @@ }, "node_modules/esquery": { "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { "estraverse": "^5.1.0" }, @@ -4891,18 +6167,11 @@ "node": ">=0.10" } }, - "node_modules/esquery/node_modules/estraverse": { - "version": "5.3.0", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, "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, - "license": "BSD-2-Clause", "dependencies": { "estraverse": "^5.2.0" }, @@ -4910,48 +6179,51 @@ "node": ">=4.0" } }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.3.0", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, "node_modules/estraverse": { - "version": "4.3.0", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, - "license": "BSD-2-Clause", "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, - "license": "BSD-2-Clause", "engines": { "node": ">=0.10.0" } }, "node_modules/etag": { "version": "1.8.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "engines": { "node": ">= 0.6" } }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "dev": true + }, "node_modules/events": { "version": "3.3.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "engines": { "node": ">=0.8.x" } }, "node_modules/execa": { "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", "dev": true, - "license": "MIT", "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", @@ -4970,36 +6242,26 @@ "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/execa/node_modules/cross-spawn": { - "version": "7.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/exit": { "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", "dev": true, "engines": { "node": ">= 0.8.0" } }, "node_modules/expect": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", "dev": true, - "license": "MIT", "dependencies": { - "@jest/expect-utils": "^29.5.0", - "jest-get-type": "^29.4.3", - "jest-matcher-utils": "^29.5.0", - "jest-message-util": "^29.5.0", - "jest-util": "^29.5.0" + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -5007,7 +6269,8 @@ }, "node_modules/express": { "version": "4.18.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", @@ -5046,12 +6309,14 @@ } }, "node_modules/express-async-handler": { - "version": "1.1.4", - "license": "MIT" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/express-async-handler/-/express-async-handler-1.2.0.tgz", + "integrity": "sha512-rCSVtPXRmQSW8rmik/AIb2P0op6l7r1fMW538yyvTMltCO4xQEWMmobfrIxN2V1/mVrgxB8Az3reYF6yUZw37w==" }, "node_modules/express-session": { "version": "1.17.3", - "license": "MIT", + "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.3.tgz", + "integrity": "sha512-4+otWXlShYlG1Ma+2Jnn+xgKUZTMJ5QD3YvfilX3AcocOAbIkVylSWEklzALe/+Pu4qV6TYBj5GwOBFfdKqLBw==", "dependencies": { "cookie": "0.4.2", "cookie-signature": "1.0.6", @@ -5068,24 +6333,29 @@ }, "node_modules/express-session/node_modules/cookie": { "version": "0.4.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", "engines": { "node": ">= 0.6" } }, "node_modules/express-session/node_modules/debug": { "version": "2.6.9", - "license": "MIT", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dependencies": { "ms": "2.0.0" } }, "node_modules/express-session/node_modules/ms": { "version": "2.0.0", - "license": "MIT" + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "node_modules/express-session/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", @@ -5099,16 +6369,17 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "license": "MIT" + ] }, "node_modules/express-sslify": { "version": "1.2.0", - "license": "MIT" + "resolved": "https://registry.npmjs.org/express-sslify/-/express-sslify-1.2.0.tgz", + "integrity": "sha512-OOf2B3MxAVjEXPPWl4Z19wA2oMH+RCULJVhejPwuhiDDClr9QczZz5ycABLSnnN+oY8JcLs32ghs9cxOj0vi+w==" }, "node_modules/express/node_modules/body-parser": { "version": "1.20.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", "dependencies": { "bytes": "3.1.2", "content-type": "~1.0.4", @@ -5130,28 +6401,21 @@ }, "node_modules/express/node_modules/debug": { "version": "2.6.9", - "license": "MIT", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dependencies": { "ms": "2.0.0" } }, "node_modules/express/node_modules/ms": { "version": "2.0.0", - "license": "MIT" - }, - "node_modules/express/node_modules/on-finished": { - "version": "2.4.1", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "node_modules/express/node_modules/raw-body": { "version": "2.5.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", @@ -5164,6 +6428,8 @@ }, "node_modules/express/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", @@ -5177,28 +6443,34 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "license": "MIT" + ] }, "node_modules/fast-deep-equal": { "version": "3.1.3", - "dev": true, - "license": "MIT" + "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.2.0", - "dev": true, - "license": "Apache-2.0" + "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-equals": { - "version": "4.0.3", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.0.1.tgz", + "integrity": "sha512-WF1Wi8PwwSY7/6Kx0vKXtw8RwuSGoM1bvDaJbu7MxDlR1vovZjIAKrnzyrThgAjm6JDTu0fVgWXDlMGspodfoQ==", "dev": true, - "license": "MIT" + "engines": { + "node": ">=6.0.0" + } }, "node_modules/fast-glob": { - "version": "3.2.12", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, - "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -5212,8 +6484,9 @@ }, "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, - "license": "ISC", "dependencies": { "is-glob": "^4.0.1" }, @@ -5223,30 +6496,34 @@ }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", - "license": "MIT" + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, "node_modules/fast-levenshtein": { "version": "2.0.6", - "dev": true, - "license": "MIT" + "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", - "license": "MIT" + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" }, "node_modules/fast-xml-parser": { - "version": "4.2.2", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.3.3.tgz", + "integrity": "sha512-coV/D1MhrShMvU6D0I+VAK3umz6hUaxxhL0yp/9RjfiYUfAv14rDhGQL+PLForhMdr0wq3PiV07WtkkNjJjNHg==", "funding": [ - { - "type": "paypal", - "url": "https://paypal.me/naturalintelligence" - }, { "type": "github", "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" } ], - "license": "MIT", "dependencies": { "strnum": "^1.0.5" }, @@ -5255,40 +6532,45 @@ } }, "node_modules/fastq": { - "version": "1.15.0", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.16.0.tgz", + "integrity": "sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA==", "dev": true, - "license": "ISC", "dependencies": { "reusify": "^1.0.4" } }, "node_modules/fb-watchman": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", "dev": true, - "license": "Apache-2.0", "dependencies": { "bser": "2.1.1" } }, "node_modules/file-entry-cache": { - "version": "6.0.1", + "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": "^3.0.4" + "flat-cache": "^4.0.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=16.0.0" } }, "node_modules/file-size": { "version": "1.0.0", - "license": "MIT" + "resolved": "https://registry.npmjs.org/file-size/-/file-size-1.0.0.tgz", + "integrity": "sha512-tLIdonWTpABkU6Axg2yGChYdrOsy4V8xcm0IcyAP8fSsu6jiXLm5pgs083e4sq5fzNRZuAYolUbZyYmPvCKfwQ==" }, "node_modules/fill-range": { "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dev": true, - "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -5298,7 +6580,8 @@ }, "node_modules/finalhandler": { "version": "1.2.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", "dependencies": { "debug": "2.6.9", "encodeurl": "~1.0.2", @@ -5314,29 +6597,22 @@ }, "node_modules/finalhandler/node_modules/debug": { "version": "2.6.9", - "license": "MIT", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dependencies": { "ms": "2.0.0" } }, "node_modules/finalhandler/node_modules/ms": { "version": "2.0.0", - "license": "MIT" - }, - "node_modules/finalhandler/node_modules/on-finished": { - "version": "2.4.1", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "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, - "license": "MIT", "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -5348,32 +6624,48 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/find-up-simple": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/find-up-simple/-/find-up-simple-1.0.0.tgz", + "integrity": "sha512-q7Us7kcjj2VMePAa02hDAF6d+MzsdsAWEwYyOpwUtlerRBkOEPBCRZrAV4XfcSN8fHAgaD0hP7miwoay6DCprw==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/flat-cache": { - "version": "3.0.4", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.0.tgz", + "integrity": "sha512-EryKbCE/wxpxKniQlyas6PY1I9vwtF3uCBweX+N8KYTCn3Y12RTGtQAJ/bd5pl7kxUAc8v/R3Ake/N17OZiFqA==", "dev": true, - "license": "MIT", "dependencies": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" + "flatted": "^3.2.9", + "keyv": "^4.5.4", + "rimraf": "^5.0.5" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=16" } }, "node_modules/flatted": { - "version": "3.2.5", - "dev": true, - "license": "ISC" + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", + "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", + "dev": true }, "node_modules/follow-redirects": { - "version": "1.15.2", + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", + "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", "funding": [ { "type": "individual", "url": "https://github.com/sponsors/RubenVerborgh" } ], - "license": "MIT", "engines": { "node": ">=4.0" }, @@ -5385,14 +6677,44 @@ }, "node_modules/for-each": { "version": "0.3.3", - "license": "MIT", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", "dependencies": { "is-callable": "^1.1.3" } }, + "node_modules/foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "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.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -5404,26 +6726,31 @@ }, "node_modules/forwarded": { "version": "0.2.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "engines": { "node": ">= 0.6" } }, "node_modules/fresh": { "version": "0.5.2", - "license": "MIT", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", "engines": { "node": ">= 0.6" } }, "node_modules/fs.realpath": { "version": "1.0.0", - "license": "ISC" + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "node_modules/fsevents": { - "version": "2.3.2", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, - "license": "MIT", + "hasInstallScript": true, "optional": true, "os": [ "darwin" @@ -5433,47 +6760,69 @@ } }, "node_modules/function-bind": { - "version": "1.1.1", - "license": "MIT" + "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/generic-pool": { "version": "3.9.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", + "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==", "engines": { "node": ">= 4" } }, "node_modules/gensequence": { - "version": "5.0.2", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/gensequence/-/gensequence-6.0.0.tgz", + "integrity": "sha512-8WwuywE9pokJRAcg2QFR/plk3cVPebSUqRPzpGQh3WQ0wIiHAw+HyOQj5IuHyUTQBHpBKFoB2JUMu9zT3vJ16Q==", "dev": true, - "license": "MIT", "engines": { - "node": ">=14" + "node": ">=16" } }, "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, - "license": "MIT", "engines": { "node": ">=6.9.0" } }, "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==", "dev": true, - "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/get-intrinsic": { + "node_modules/get-east-asian-width": { "version": "1.2.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.2.0.tgz", + "integrity": "sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", + "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -5481,18 +6830,20 @@ }, "node_modules/get-package-type": { "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", "dev": true, - "license": "MIT", "engines": { "node": ">=8.0.0" } }, "node_modules/get-stdin": { - "version": "8.0.0", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-9.0.0.tgz", + "integrity": "sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA==", "dev": true, - "license": "MIT", "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -5500,8 +6851,9 @@ }, "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, - "license": "MIT", "engines": { "node": ">=10" }, @@ -5509,18 +6861,32 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/get-tsconfig": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.2.tgz", + "integrity": "sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==", + "dev": true, + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, "node_modules/github-username-regex": { "version": "1.0.0", - "license": "CC0-1.0" + "resolved": "https://registry.npmjs.org/github-username-regex/-/github-username-regex-1.0.0.tgz", + "integrity": "sha512-EqDVkN0/5MQyDPOSDLInVRRXdeISRfcN1UW/1FUqD2knV1HHw8DndMB3UPNn5lO51DvRnjzbLXwWqNNV86PLOw==" }, "node_modules/glob": { - "version": "7.2.0", - "license": "ISC", + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" }, @@ -5533,8 +6899,9 @@ }, "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, - "license": "ISC", "dependencies": { "is-glob": "^4.0.3" }, @@ -5542,21 +6909,46 @@ "node": ">=10.13.0" } }, - "node_modules/global-dirs": { - "version": "0.1.1", + "node_modules/glob/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/glob/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/global-directory": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-4.0.1.tgz", + "integrity": "sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==", "dev": true, - "license": "MIT", "dependencies": { - "ini": "^1.3.4" + "ini": "4.1.1" }, "engines": { - "node": ">=4" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/globals": { - "version": "13.20.0", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, - "license": "MIT", "dependencies": { "type-fest": "^0.20.2" }, @@ -5569,8 +6961,9 @@ }, "node_modules/globby": { "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, - "license": "MIT", "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", @@ -5588,7 +6981,8 @@ }, "node_modules/gopd": { "version": "1.0.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", "dependencies": { "get-intrinsic": "^1.1.3" }, @@ -5597,48 +6991,60 @@ } }, "node_modules/graceful-fs": { - "version": "4.2.10", - "license": "ISC" - }, - "node_modules/grapheme-splitter": { - "version": "1.0.4", - "dev": true, - "license": "MIT" + "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/graphemer": { "version": "1.4.0", - "dev": true, - "license": "MIT" - }, - "node_modules/has": { - "version": "1.0.3", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true }, "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, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/has-own-prop": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-own-prop/-/has-own-prop-2.0.0.tgz", + "integrity": "sha512-Pq0h+hvsVm6dDEa8x82GnLSYHOzNDt7f0ddFa3FqcQlgzEiptPqL+XrOJNavjOzSYiYWIrgeVYYgGlLmnxwilQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, + "node_modules/has-property-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", + "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", + "dependencies": { + "get-intrinsic": "^1.2.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-symbols": { "version": "1.0.3", - "license": "MIT", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "engines": { "node": ">= 0.4" }, @@ -5648,7 +7054,8 @@ }, "node_modules/has-tostringtag": { "version": "1.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", "dependencies": { "has-symbols": "^1.0.2" }, @@ -5659,9 +7066,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/hsts": { "version": "2.2.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/hsts/-/hsts-2.2.0.tgz", + "integrity": "sha512-ToaTnQ2TbJkochoVcdXYm4HOCliNozlviNsg+X2XQLQvZNI/kCHR9rZxVYpJB3UPcHz80PgxRyWQ7PdU1r+VBQ==", "dependencies": { "depd": "2.0.0" }, @@ -5671,12 +7090,14 @@ }, "node_modules/html-escaper": { "version": "2.0.2", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true }, "node_modules/http-errors": { "version": "2.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", @@ -5690,7 +7111,8 @@ }, "node_modules/http-proxy-agent": { "version": "5.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", "dependencies": { "@tootallnate/once": "2", "agent-base": "6", @@ -5701,8 +7123,9 @@ } }, "node_modules/https-proxy-agent": { - "version": "5.0.0", - "license": "MIT", + "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" @@ -5713,16 +7136,18 @@ }, "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, - "license": "Apache-2.0", "engines": { "node": ">=10.17.0" } }, "node_modules/husky": { "version": "8.0.3", + "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz", + "integrity": "sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==", "dev": true, - "license": "MIT", "bin": { "husky": "lib/bin.js" }, @@ -5735,7 +7160,8 @@ }, "node_modules/iconv-lite": { "version": "0.4.24", - "license": "MIT", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dependencies": { "safer-buffer": ">= 2.1.2 < 3" }, @@ -5745,6 +7171,8 @@ }, "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", @@ -5758,21 +7186,22 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "license": "BSD-3-Clause" + ] }, "node_modules/ignore": { - "version": "5.2.0", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", + "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", "dev": true, - "license": "MIT", "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==", "dev": true, - "license": "MIT", "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -5786,8 +7215,9 @@ }, "node_modules/import-fresh/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, - "license": "MIT", "dependencies": { "callsites": "^3.0.0" }, @@ -5797,16 +7227,29 @@ }, "node_modules/import-fresh/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, - "license": "MIT", "engines": { "node": ">=4" } }, + "node_modules/import-in-the-middle": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.4.2.tgz", + "integrity": "sha512-9WOz1Yh/cvO/p69sxRmhyQwrIGGSp7EIdcb+fFNVi7CzQGQB8U1/1XrKVSbEd/GNOAeM0peJtmi7+qphe7NvAw==", + "dependencies": { + "acorn": "^8.8.2", + "acorn-import-assertions": "^1.9.0", + "cjs-module-lexer": "^1.2.2", + "module-details-from-path": "^1.0.3" + } + }, "node_modules/import-local": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", "dev": true, - "license": "MIT", "dependencies": { "pkg-dir": "^4.2.0", "resolve-cwd": "^3.0.0" @@ -5822,9 +7265,10 @@ } }, "node_modules/import-meta-resolve": { - "version": "2.2.2", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.0.0.tgz", + "integrity": "sha512-okYUR7ZQPH+efeuMJGlq4f8ubUgO50kByRPyt/Cy1Io4PSRsPjxME+YlVaCOx+NIToW7hCsZNFJyTPFFKepRSA==", "dev": true, - "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" @@ -5832,23 +7276,17 @@ }, "node_modules/imurmurhash": { "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.8.19" } }, - "node_modules/indent-string": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/inflight": { "version": "1.0.6", - "license": "ISC", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -5856,23 +7294,30 @@ }, "node_modules/inherits": { "version": "2.0.4", - "license": "ISC" + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/ini": { - "version": "1.3.8", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz", + "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==", "dev": true, - "license": "ISC" + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } }, "node_modules/ipaddr.js": { "version": "1.9.1", - "license": "MIT", + "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-arguments": { "version": "1.1.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", "dependencies": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" @@ -5886,10 +7331,13 @@ }, "node_modules/is-arrayish": { "version": "0.2.1", - "license": "MIT" + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" }, "node_modules/is-buffer": { "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", "funding": [ { "type": "github", @@ -5904,14 +7352,29 @@ "url": "https://feross.org/support" } ], - "license": "MIT", "engines": { "node": ">=4" } }, + "node_modules/is-builtin-module": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", + "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", + "dev": true, + "dependencies": { + "builtin-modules": "^3.3.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-callable": { "version": "1.2.7", - "license": "MIT", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "engines": { "node": ">= 0.4" }, @@ -5920,10 +7383,11 @@ } }, "node_modules/is-core-module": { - "version": "2.12.0", - "license": "MIT", + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", "dependencies": { - "has": "^1.0.3" + "hasown": "^2.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -5931,7 +7395,8 @@ }, "node_modules/is-docker": { "version": "2.2.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", "bin": { "is-docker": "cli.js" }, @@ -5944,7 +7409,8 @@ }, "node_modules/is-expression": { "version": "4.0.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-4.0.0.tgz", + "integrity": "sha512-zMIXX63sxzG3XrkHkrAPvm/OVZVSCPNkwMHU8oTX7/U3AL78I0QXCEICXUM13BIa8TYGZ68PiTKfQz3yaTNr4A==", "dependencies": { "acorn": "^7.1.1", "object-assign": "^4.1.1" @@ -5952,7 +7418,8 @@ }, "node_modules/is-expression/node_modules/acorn": { "version": "7.4.1", - "license": "MIT", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", "bin": { "acorn": "bin/acorn" }, @@ -5962,31 +7429,38 @@ }, "node_modules/is-extglob": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", "dev": true, - "license": "MIT", "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/is-generator-fn": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/is-generator-function": { "version": "1.0.10", - "license": "MIT", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -5999,8 +7473,9 @@ }, "node_modules/is-glob": { "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, - "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" }, @@ -6010,42 +7485,40 @@ }, "node_modules/is-number": { "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.12.0" } }, "node_modules/is-obj": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/is-path-inside": { "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/is-plain-object": { - "version": "5.0.0", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/is-promise": { "version": "2.2.2", - "license": "MIT" + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==" }, "node_modules/is-regex": { "version": "1.1.4", - "license": "MIT", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", "dependencies": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" @@ -6059,8 +7532,9 @@ }, "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==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" }, @@ -6069,14 +7543,11 @@ } }, "node_modules/is-typed-array": { - "version": "1.1.10", - "license": "MIT", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", + "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" + "which-typed-array": "^1.1.11" }, "engines": { "node": ">= 0.4" @@ -6087,12 +7558,14 @@ }, "node_modules/is-typedarray": { "version": "1.0.0", - "dev": true, - "license": "MIT" + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "dev": true }, "node_modules/is-wsl": { "version": "2.2.0", - "license": "MIT", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", "dependencies": { "is-docker": "^2.0.0" }, @@ -6102,57 +7575,54 @@ }, "node_modules/isexe": { "version": "2.0.0", - "dev": true, - "license": "ISC" + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true }, "node_modules/istanbul-lib-coverage": { - "version": "3.2.0", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", "dev": true, - "license": "BSD-3-Clause", "engines": { "node": ">=8" } }, "node_modules/istanbul-lib-instrument": { - "version": "5.2.1", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.1.tgz", + "integrity": "sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { "@babel/core": "^7.12.3", "@babel/parser": "^7.14.7", "@istanbuljs/schema": "^0.1.2", "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" + "semver": "^7.5.4" }, "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "6.3.0", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "node": ">=10" } }, "node_modules/istanbul-lib-report": { - "version": "3.0.0", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", + "make-dir": "^4.0.0", "supports-color": "^7.1.0" }, "engines": { - "node": ">=8" + "node": ">=10" } }, "node_modules/istanbul-lib-source-maps": { "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { "debug": "^4.1.1", "istanbul-lib-coverage": "^3.0.0", @@ -6163,9 +7633,10 @@ } }, "node_modules/istanbul-reports": { - "version": "3.1.5", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", + "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { "html-escaper": "^2.0.0", "istanbul-lib-report": "^3.0.0" @@ -6174,15 +7645,34 @@ "node": ">=8" } }, + "node_modules/jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, "node_modules/jest": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "dev": true, - "license": "MIT", "dependencies": { - "@jest/core": "^29.5.0", - "@jest/types": "^29.5.0", + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", "import-local": "^3.0.2", - "jest-cli": "^29.5.0" + "jest-cli": "^29.7.0" }, "bin": { "jest": "bin/jest.js" @@ -6200,11 +7690,13 @@ } }, "node_modules/jest-changed-files": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", "dev": true, - "license": "MIT", "dependencies": { "execa": "^5.0.0", + "jest-util": "^29.7.0", "p-limit": "^3.1.0" }, "engines": { @@ -6212,27 +7704,28 @@ } }, "node_modules/jest-circus": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", "dev": true, - "license": "MIT", "dependencies": { - "@jest/environment": "^29.5.0", - "@jest/expect": "^29.5.0", - "@jest/test-result": "^29.5.0", - "@jest/types": "^29.5.0", + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "co": "^4.6.0", - "dedent": "^0.7.0", + "dedent": "^1.0.0", "is-generator-fn": "^2.0.0", - "jest-each": "^29.5.0", - "jest-matcher-utils": "^29.5.0", - "jest-message-util": "^29.5.0", - "jest-runtime": "^29.5.0", - "jest-snapshot": "^29.5.0", - "jest-util": "^29.5.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", "p-limit": "^3.1.0", - "pretty-format": "^29.5.0", + "pretty-format": "^29.7.0", "pure-rand": "^6.0.0", "slash": "^3.0.0", "stack-utils": "^2.0.3" @@ -6241,22 +7734,71 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/jest-circus/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/jest-circus/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/jest-circus/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/jest-circus/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/jest-cli": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", "dev": true, - "license": "MIT", "dependencies": { - "@jest/core": "^29.5.0", - "@jest/test-result": "^29.5.0", - "@jest/types": "^29.5.0", + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", "chalk": "^4.0.0", + "create-jest": "^29.7.0", "exit": "^0.1.2", - "graceful-fs": "^4.2.9", "import-local": "^3.0.2", - "jest-config": "^29.5.0", - "jest-util": "^29.5.0", - "jest-validate": "^29.5.0", - "prompts": "^2.0.1", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", "yargs": "^17.3.1" }, "bin": { @@ -6274,31 +7816,81 @@ } } }, + "node_modules/jest-cli/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/jest-cli/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/jest-cli/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/jest-cli/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/jest-config": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", "dev": true, - "license": "MIT", "dependencies": { "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.5.0", - "@jest/types": "^29.5.0", - "babel-jest": "^29.5.0", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", "chalk": "^4.0.0", "ci-info": "^3.2.0", "deepmerge": "^4.2.2", "glob": "^7.1.3", "graceful-fs": "^4.2.9", - "jest-circus": "^29.5.0", - "jest-environment-node": "^29.5.0", - "jest-get-type": "^29.4.3", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.5.0", - "jest-runner": "^29.5.0", - "jest-util": "^29.5.0", - "jest-validate": "^29.5.0", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", "micromatch": "^4.0.4", "parse-json": "^5.2.0", - "pretty-format": "^29.5.0", + "pretty-format": "^29.7.0", "slash": "^3.0.0", "strip-json-comments": "^3.1.1" }, @@ -6318,24 +7910,124 @@ } } }, + "node_modules/jest-config/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/jest-config/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/jest-config/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/jest-config/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/jest-diff": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", "dev": true, - "license": "MIT", "dependencies": { "chalk": "^4.0.0", - "diff-sequences": "^29.4.3", - "jest-get-type": "^29.4.3", - "pretty-format": "^29.5.0" + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/jest-diff/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/jest-diff/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/jest-diff/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/jest-diff/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/jest-docblock": { - "version": "29.4.3", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", "dev": true, - "license": "MIT", "dependencies": { "detect-newline": "^3.0.0" }, @@ -6344,58 +8036,111 @@ } }, "node_modules/jest-each": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", "dev": true, - "license": "MIT", "dependencies": { - "@jest/types": "^29.5.0", + "@jest/types": "^29.6.3", "chalk": "^4.0.0", - "jest-get-type": "^29.4.3", - "jest-util": "^29.5.0", - "pretty-format": "^29.5.0" + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/jest-each/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/jest-each/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/jest-each/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/jest-each/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/jest-environment-node": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", "dev": true, - "license": "MIT", "dependencies": { - "@jest/environment": "^29.5.0", - "@jest/fake-timers": "^29.5.0", - "@jest/types": "^29.5.0", + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", "@types/node": "*", - "jest-mock": "^29.5.0", - "jest-util": "^29.5.0" + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-get-type": { - "version": "29.4.3", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", "dev": true, - "license": "MIT", "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-haste-map": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", "dev": true, - "license": "MIT", "dependencies": { - "@jest/types": "^29.5.0", + "@jest/types": "^29.6.3", "@types/graceful-fs": "^4.1.3", "@types/node": "*", "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.4.3", - "jest-util": "^29.5.0", - "jest-worker": "^29.5.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", "micromatch": "^4.0.4", "walker": "^1.0.8" }, @@ -6408,8 +8153,9 @@ }, "node_modules/jest-junit": { "version": "16.0.0", + "resolved": "https://registry.npmjs.org/jest-junit/-/jest-junit-16.0.0.tgz", + "integrity": "sha512-A94mmw6NfJab4Fg/BlvVOUXzXgF0XIH6EmTgJ5NDPp4xoKq0Kr7sErb+4Xs9nZvu58pJojz5RFGpqnZYJTrRfQ==", "dev": true, - "license": "Apache-2.0", "dependencies": { "mkdirp": "^1.0.4", "strip-ansi": "^6.0.1", @@ -6420,44 +8166,117 @@ "node": ">=10.12.0" } }, - "node_modules/jest-leak-detector": { - "version": "29.5.0", + "node_modules/jest-junit/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, - "license": "MIT", - "dependencies": { - "jest-get-type": "^29.4.3", - "pretty-format": "^29.5.0" - }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=8" } }, - "node_modules/jest-matcher-utils": { - "version": "29.5.0", + "node_modules/jest-junit/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, - "license": "MIT", "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^29.5.0", - "jest-get-type": "^29.4.3", - "pretty-format": "^29.5.0" + "ansi-regex": "^5.0.1" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=8" + } + }, + "node_modules/jest-leak-detector": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils/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/jest-matcher-utils/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/jest-matcher-utils/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/jest-matcher-utils/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/jest-message-util": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", "dev": true, - "license": "MIT", "dependencies": { "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.5.0", + "@jest/types": "^29.6.3", "@types/stack-utils": "^2.0.0", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "micromatch": "^4.0.4", - "pretty-format": "^29.5.0", + "pretty-format": "^29.7.0", "slash": "^3.0.0", "stack-utils": "^2.0.3" }, @@ -6465,14 +8284,64 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/jest-message-util/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/jest-message-util/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/jest-message-util/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/jest-message-util/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/jest-mock": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", "dev": true, - "license": "MIT", "dependencies": { - "@jest/types": "^29.5.0", + "@jest/types": "^29.6.3", "@types/node": "*", - "jest-util": "^29.5.0" + "jest-util": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -6480,8 +8349,9 @@ }, "node_modules/jest-pnp-resolver": { "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", "dev": true, - "license": "MIT", "engines": { "node": ">=6" }, @@ -6495,24 +8365,26 @@ } }, "node_modules/jest-regex-util": { - "version": "29.4.3", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", "dev": true, - "license": "MIT", "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-resolve": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", "dev": true, - "license": "MIT", "dependencies": { "chalk": "^4.0.0", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.5.0", + "jest-haste-map": "^29.7.0", "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.5.0", - "jest-validate": "^29.5.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", "resolve": "^1.20.0", "resolve.exports": "^2.0.0", "slash": "^3.0.0" @@ -6522,915 +8394,955 @@ } }, "node_modules/jest-resolve-dependencies": { - "version": "29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", "dev": true, - "license": "MIT", "dependencies": { - "jest-regex-util": "^29.4.3", - "jest-snapshot": "^29.5.0" + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-runner": { - "version": "29.5.0", + "node_modules/jest-resolve/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, - "license": "MIT", "dependencies": { - "@jest/console": "^29.5.0", - "@jest/environment": "^29.5.0", - "@jest/test-result": "^29.5.0", - "@jest/transform": "^29.5.0", - "@jest/types": "^29.5.0", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.4.3", - "jest-environment-node": "^29.5.0", - "jest-haste-map": "^29.5.0", - "jest-leak-detector": "^29.5.0", - "jest-message-util": "^29.5.0", - "jest-resolve": "^29.5.0", - "jest-runtime": "^29.5.0", - "jest-util": "^29.5.0", - "jest-watcher": "^29.5.0", - "jest-worker": "^29.5.0", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" + "color-convert": "^2.0.1" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-runtime": { - "version": "29.5.0", + "node_modules/jest-resolve/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, - "license": "MIT", "dependencies": { - "@jest/environment": "^29.5.0", - "@jest/fake-timers": "^29.5.0", - "@jest/globals": "^29.5.0", - "@jest/source-map": "^29.4.3", - "@jest/test-result": "^29.5.0", - "@jest/transform": "^29.5.0", - "@jest/types": "^29.5.0", - "@types/node": "*", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.5.0", - "jest-message-util": "^29.5.0", - "jest-mock": "^29.5.0", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.5.0", - "jest-snapshot": "^29.5.0", - "jest-util": "^29.5.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-snapshot": { - "version": "29.5.0", + "node_modules/jest-resolve/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, - "license": "MIT", "dependencies": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/traverse": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.5.0", - "@jest/transform": "^29.5.0", - "@jest/types": "^29.5.0", - "@types/babel__traverse": "^7.0.6", - "@types/prettier": "^2.1.5", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.5.0", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.5.0", - "jest-get-type": "^29.4.3", - "jest-matcher-utils": "^29.5.0", - "jest-message-util": "^29.5.0", - "jest-util": "^29.5.0", - "natural-compare": "^1.4.0", - "pretty-format": "^29.5.0", - "semver": "^7.3.5" + "color-name": "~1.1.4" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=7.0.0" } }, - "node_modules/jest-util": { - "version": "29.5.0", + "node_modules/jest-resolve/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/jest-runner": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", "dev": true, - "license": "MIT", "dependencies": { - "@jest/types": "^29.5.0", + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", - "ci-info": "^3.2.0", + "emittery": "^0.13.1", "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-validate": { - "version": "29.5.0", + "node_modules/jest-runner/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, - "license": "MIT", "dependencies": { - "@jest/types": "^29.5.0", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.4.3", - "leven": "^3.1.0", - "pretty-format": "^29.5.0" + "color-convert": "^2.0.1" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-validate/node_modules/camelcase": { - "version": "6.3.0", + "node_modules/jest-runner/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, - "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-watcher": { - "version": "29.5.0", + "node_modules/jest-runner/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, - "license": "MIT", "dependencies": { - "@jest/test-result": "^29.5.0", - "@jest/types": "^29.5.0", + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-runner/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/jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", "@types/node": "*", - "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.5.0", - "string-length": "^4.0.1" + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-worker": { - "version": "29.5.0", + "node_modules/jest-runtime/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, - "license": "MIT", "dependencies": { - "@types/node": "*", - "jest-util": "^29.5.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" + "color-convert": "^2.0.1" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", + "node_modules/jest-runtime/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, - "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/joi": { - "version": "17.9.2", - "license": "BSD-3-Clause", + "node_modules/jest-runtime/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": { - "@hapi/hoek": "^9.0.0", - "@hapi/topo": "^5.0.0", - "@sideway/address": "^4.1.3", - "@sideway/formula": "^3.0.1", - "@sideway/pinpoint": "^2.0.0" - } - }, - "node_modules/joi/node_modules/@hapi/hoek": { - "version": "9.3.0", - "license": "BSD-3-Clause" - }, - "node_modules/jose": { - "version": "4.14.4", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/panva" + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, - "node_modules/js-stringify": { - "version": "1.0.2", - "license": "MIT" + "node_modules/jest-runtime/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/js-tokens": { - "version": "4.0.0", + "node_modules/jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", "dev": true, - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "license": "MIT", "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsbi": { - "version": "3.2.5", - "license": "Apache-2.0" - }, - "node_modules/jsesc": { - "version": "2.5.2", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" }, "engines": { - "node": ">=4" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/json-2-csv": { - "version": "3.18.0", - "license": "MIT", + "node_modules/jest-snapshot/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": { - "deeks": "2.6.0", - "doc-path": "3.0.6" + "color-convert": "^2.0.1" }, "engines": { - "node": ">= 12" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/json-parse-better-errors": { - "version": "1.0.2", - "license": "MIT" - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", + "node_modules/jest-snapshot/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, - "license": "MIT" + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", + "node_modules/jest-snapshot/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, - "license": "MIT" + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "dev": true, - "license": "MIT" + "node_modules/jest-snapshot/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/json5": { - "version": "2.2.3", + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", "dev": true, - "license": "MIT", - "bin": { - "json5": "lib/cli.js" + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" }, "engines": { - "node": ">=6" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jsonc": { - "version": "2.0.0", - "license": "MIT", + "node_modules/jest-util/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": { - "fast-safe-stringify": "^2.0.6", - "graceful-fs": "^4.1.15", - "mkdirp": "^0.5.1", - "parse-json": "^4.0.0", - "strip-bom": "^4.0.0", - "strip-json-comments": "^3.0.1" + "color-convert": "^2.0.1" }, "engines": { "node": ">=8" - } - }, - "node_modules/jsonc/node_modules/mkdirp": { - "version": "0.5.6", - "license": "MIT", - "dependencies": { - "minimist": "^1.2.6" }, - "bin": { - "mkdirp": "bin/cmd.js" + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jsonc/node_modules/parse-json": { - "version": "4.0.0", - "license": "MIT", + "node_modules/jest-util/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": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jsonwebtoken": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.1.tgz", - "integrity": "sha512-K8wx7eJ5TPvEjuiVSkv167EVboBDv9PZdDoF7BgeQnBLVvZWW9clr2PsQHVJDTKaEIH5JBIwHujGcHp7GgI2eg==", + "node_modules/jest-util/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": { - "jws": "^3.2.2", - "lodash": "^4.17.21", - "ms": "^2.1.1", - "semver": "^7.3.8" + "color-name": "~1.1.4" }, "engines": { - "node": ">=12", - "npm": ">=6" + "node": ">=7.0.0" } }, - "node_modules/jsonwebtoken/node_modules/jwa": { - "version": "1.4.1", - "license": "MIT", - "dependencies": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } + "node_modules/jest-util/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/jsonwebtoken/node_modules/jws": { - "version": "3.2.2", - "license": "MIT", + "node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, "dependencies": { - "jwa": "^1.4.1", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/jssha": { - "version": "3.3.0", - "license": "BSD-3-Clause", + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, "engines": { - "node": "*" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jstransformer": { - "version": "1.0.0", - "license": "MIT", + "node_modules/jest-validate/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": { - "is-promise": "^2.0.0", - "promise": "^7.0.1" + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jwa": { - "version": "2.0.0", - "license": "MIT", - "dependencies": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" + "node_modules/jest-validate/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/jwks-rsa": { - "version": "3.0.1", - "license": "MIT", + "node_modules/jest-validate/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": { - "@types/express": "^4.17.14", - "@types/jsonwebtoken": "^9.0.0", - "debug": "^4.3.4", - "jose": "^4.10.4", - "limiter": "^1.1.5", - "lru-memoizer": "^2.1.4" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">=14" - } - }, - "node_modules/jws": { - "version": "4.0.0", - "license": "MIT", - "dependencies": { - "jwa": "^2.0.0", - "safe-buffer": "^5.0.1" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/keypress": { - "version": "0.2.1", - "license": "MIT" - }, - "node_modules/kleur": { - "version": "3.0.3", + "node_modules/jest-validate/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, - "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, "engines": { - "node": ">=6" + "node": ">=7.0.0" } }, - "node_modules/language-map": { - "version": "1.5.0", - "license": "MIT" + "node_modules/jest-validate/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/leven": { - "version": "3.1.0", + "node_modules/jest-watcher": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", "dev": true, - "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" + }, "engines": { - "node": ">=6" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/levn": { - "version": "0.4.1", + "node_modules/jest-watcher/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, - "license": "MIT", "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" + "color-convert": "^2.0.1" }, "engines": { - "node": ">= 0.8.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/lilconfig": { - "version": "2.1.0", + "node_modules/jest-watcher/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, - "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/limiter": { - "version": "1.1.5" - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "dev": true, - "license": "MIT" - }, - "node_modules/linkify-it": { - "version": "4.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "uc.micro": "^1.0.1" - } - }, - "node_modules/lint-staged": { - "version": "13.2.2", + "node_modules/jest-watcher/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, - "license": "MIT", "dependencies": { - "chalk": "5.2.0", - "cli-truncate": "^3.1.0", - "commander": "^10.0.0", - "debug": "^4.3.4", - "execa": "^7.0.0", - "lilconfig": "2.1.0", - "listr2": "^5.0.7", - "micromatch": "^4.0.5", - "normalize-path": "^3.0.0", - "object-inspect": "^1.12.3", - "pidtree": "^0.6.0", - "string-argv": "^0.3.1", - "yaml": "^2.2.2" - }, - "bin": { - "lint-staged": "bin/lint-staged.js" + "color-name": "~1.1.4" }, "engines": { - "node": "^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/lint-staged" + "node": ">=7.0.0" } }, - "node_modules/lint-staged/node_modules/chalk": { - "version": "5.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } + "node_modules/jest-watcher/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/lint-staged/node_modules/cross-spawn": { - "version": "7.0.3", + "node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", "dev": true, - "license": "MIT", "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" }, "engines": { - "node": ">= 8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/lint-staged/node_modules/execa": { - "version": "7.1.1", + "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, - "license": "MIT", "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.1", - "human-signals": "^4.3.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^3.0.7", - "strip-final-newline": "^3.0.0" + "has-flag": "^4.0.0" }, "engines": { - "node": "^14.18.0 || ^16.14.0 || >=18.0.0" + "node": ">=10" }, "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/lint-staged/node_modules/human-signals": { - "version": "4.3.1", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=14.18.0" + "node_modules/joi": { + "version": "17.12.0", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.12.0.tgz", + "integrity": "sha512-HSLsmSmXz+PV9PYoi3p7cgIbj06WnEBNT28n+bbBNcPZXZFqCzzvGqpTBPujx/Z0nh1+KNQPDrNgdmQ8dq0qYw==", + "dependencies": { + "@hapi/hoek": "^9.3.0", + "@hapi/topo": "^5.1.0", + "@sideway/address": "^4.1.4", + "@sideway/formula": "^3.0.1", + "@sideway/pinpoint": "^2.0.0" } }, - "node_modules/lint-staged/node_modules/is-stream": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, + "node_modules/joi/node_modules/@hapi/hoek": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" + }, + "node_modules/jose": { + "version": "4.15.4", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.4.tgz", + "integrity": "sha512-W+oqK4H+r5sITxfxpSU+MMdr/YSWGvgZMQDIsNoBDGGy4i7GBPTtvFKibQzW06n3U3TqHjhvBJsirShsEJ6eeQ==", "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/panva" } }, - "node_modules/lint-staged/node_modules/mimic-fn": { + "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==" + }, + "node_modules/js-tokens": { "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true }, - "node_modules/lint-staged/node_modules/npm-run-path": { - "version": "5.1.0", - "dev": true, - "license": "MIT", + "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==", "dependencies": { - "path-key": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "argparse": "^2.0.1" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/lint-staged/node_modules/npm-run-path/node_modules/path-key": { - "version": "4.0.0", + "node_modules/jsbi": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/jsbi/-/jsbi-3.2.5.tgz", + "integrity": "sha512-aBE4n43IPvjaddScbvWRA2YlTzKEynHzu7MqOyTipdHucf/VxS63ViCjxYRg86M8Rxwbt/GfzHl1kKERkt45fQ==" + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" + "bin": { + "jsesc": "bin/jsesc" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=4" } }, - "node_modules/lint-staged/node_modules/onetime": { - "version": "6.0.0", - "dev": true, - "license": "MIT", + "node_modules/json-2-csv": { + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/json-2-csv/-/json-2-csv-3.20.0.tgz", + "integrity": "sha512-IbqUB+yaycVNB/q2fiY5kyRjy5kRiEXqvNvGlxM5L0Bfi0RdvklVHc4t9MfeYF1GsZVpZWDBs9LdWmSjsQ8jvg==", "dependencies": { - "mimic-fn": "^4.0.0" + "deeks": "2.6.1", + "doc-path": "3.1.0" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 12" } }, - "node_modules/lint-staged/node_modules/strip-final-newline": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "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 }, - "node_modules/listr2": { - "version": "5.0.8", + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" + }, + "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": "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, - "license": "MIT", - "dependencies": { - "cli-truncate": "^2.1.0", - "colorette": "^2.0.19", - "log-update": "^4.0.0", - "p-map": "^4.0.0", - "rfdc": "^1.3.0", - "rxjs": "^7.8.0", - "through": "^2.3.8", - "wrap-ansi": "^7.0.0" + "bin": { + "json5": "lib/cli.js" }, "engines": { - "node": "^14.13.1 || >=16.0.0" - }, - "peerDependencies": { - "enquirer": ">= 2.3.0 < 3" - }, - "peerDependenciesMeta": { - "enquirer": { - "optional": true - } + "node": ">=6" } }, - "node_modules/listr2/node_modules/cli-truncate": { - "version": "2.1.0", - "dev": true, - "license": "MIT", + "node_modules/jsonc": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jsonc/-/jsonc-2.0.0.tgz", + "integrity": "sha512-B281bLCT2TRMQa+AQUQY5AGcqSOXBOKaYGP4wDzoA/+QswUfN8sODektbPEs9Baq7LGKun5jQbNFpzwGuVYKhw==", "dependencies": { - "slice-ansi": "^3.0.0", - "string-width": "^4.2.0" + "fast-safe-stringify": "^2.0.6", + "graceful-fs": "^4.1.15", + "mkdirp": "^0.5.1", + "parse-json": "^4.0.0", + "strip-bom": "^4.0.0", + "strip-json-comments": "^3.0.1" }, "engines": { "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/listr2/node_modules/slice-ansi": { - "version": "3.0.0", - "dev": true, - "license": "MIT", + "node_modules/jsonc/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": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" + "minimist": "^1.2.6" }, - "engines": { - "node": ">=8" + "bin": { + "mkdirp": "bin/cmd.js" } }, - "node_modules/locate-path": { - "version": "6.0.0", - "dev": true, - "license": "MIT", + "node_modules/jsonc/node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", "dependencies": { - "p-locate": "^5.0.0" + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=4" } }, - "node_modules/lodash": { - "version": "4.17.21", - "license": "MIT" - }, - "node_modules/lodash.clonedeep": { - "version": "4.5.0", - "license": "MIT" - }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "dev": true, - "license": "MIT" - }, - "node_modules/log-update": { - "version": "4.0.0", - "dev": true, - "license": "MIT", + "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": { - "ansi-escapes": "^4.3.0", - "cli-cursor": "^3.1.0", - "slice-ansi": "^4.0.0", - "wrap-ansi": "^6.2.0" + "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": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=12", + "npm": ">=6" } }, - "node_modules/log-update/node_modules/slice-ansi": { - "version": "4.0.0", - "dev": true, - "license": "MIT", + "node_modules/jsonwebtoken/node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" } }, - "node_modules/log-update/node_modules/wrap-ansi": { - "version": "6.2.0", - "dev": true, - "license": "MIT", + "node_modules/jsonwebtoken/node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jssha": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jssha/-/jssha-3.3.1.tgz", + "integrity": "sha512-VCMZj12FCFMQYcFLPRm/0lOBbLi8uM2BhXPTqw3U4YAfs4AZfiApOoBLoN8cQE60Z50m1MYMTQVCfgF/KaCVhQ==", "engines": { - "node": ">=8" + "node": "*" } }, - "node_modules/long": { - "version": "5.2.0", - "license": "Apache-2.0" + "node_modules/jstransformer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz", + "integrity": "sha512-C9YK3Rf8q6VAPDCCU9fnqo3mAfOH6vUGnMcP4AQAYIEpWtfGLpwOTmZ+igtdK5y+VvI2n3CyYSzy4Qh34eq24A==", + "dependencies": { + "is-promise": "^2.0.0", + "promise": "^7.0.1" + } }, - "node_modules/lru-cache": { - "version": "6.0.0", - "license": "ISC", + "node_modules/jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", "dependencies": { - "yallist": "^4.0.0" + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jwks-rsa": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-3.1.0.tgz", + "integrity": "sha512-v7nqlfezb9YfHHzYII3ef2a2j1XnGeSE/bK3WfumaYCqONAIstJbrEGapz4kadScZzEt7zYCN7bucj8C0Mv/Rg==", + "dependencies": { + "@types/express": "^4.17.17", + "@types/jsonwebtoken": "^9.0.2", + "debug": "^4.3.4", + "jose": "^4.14.6", + "limiter": "^1.1.5", + "lru-memoizer": "^2.2.0" }, "engines": { - "node": ">=10" + "node": ">=14" } }, - "node_modules/lru-memoizer": { - "version": "2.1.4", - "license": "MIT", + "node_modules/jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", "dependencies": { - "lodash.clonedeep": "^4.5.0", - "lru-cache": "~4.0.0" + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" } }, - "node_modules/lru-memoizer/node_modules/lru-cache": { - "version": "4.0.2", - "license": "ISC", + "node_modules/keypress": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/keypress/-/keypress-0.2.1.tgz", + "integrity": "sha512-HjorDJFNhnM4SicvaUXac0X77NiskggxJdesG72+O5zBKpSqKFCrqmndKVqpu3pFqkla0St6uGk8Ju0sCurrmg==" + }, + "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, "dependencies": { - "pseudomap": "^1.0.1", - "yallist": "^2.0.0" + "json-buffer": "3.0.1" } }, - "node_modules/lru-memoizer/node_modules/yallist": { - "version": "2.1.2", - "license": "ISC" - }, - "node_modules/luxon": { - "version": "3.3.0", - "license": "MIT", + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, "engines": { - "node": ">=12" + "node": ">=6" } }, - "node_modules/make-dir": { + "node_modules/language-map": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/language-map/-/language-map-1.5.0.tgz", + "integrity": "sha512-n7gFZpe+DwEAX9cXVTw43i3wiudWDDtSn28RmdnS/HCPr284dQI/SztsamWanRr75oSlKSaGbV2nmWCTzGCoVg==" + }, + "node_modules/leven": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "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, - "license": "MIT", "dependencies": { - "semver": "^6.0.0" + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.8.0" } }, - "node_modules/make-dir/node_modules/semver": { - "version": "6.3.0", + "node_modules/lilconfig": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.0.0.tgz", + "integrity": "sha512-K2U4W2Ff5ibV7j7ydLr+zLAkIg5JJ4lPn1Ltsdt+Tz/IjQ8buJ55pZAxoP34lqIiwtF9iAvtLv3JGv7CAyAg+g==", "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "engines": { + "node": ">=14" } }, - "node_modules/make-error": { - "version": "1.3.6", - "dev": true, - "license": "ISC" + "node_modules/limiter": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", + "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==" }, - "node_modules/makeerror": { - "version": "1.0.12", + "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==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { - "tmpl": "1.0.5" + "uc.micro": "^2.0.0" } }, - "node_modules/markdown-it": { - "version": "13.0.1", + "node_modules/lint-staged": { + "version": "15.2.0", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.0.tgz", + "integrity": "sha512-TFZzUEV00f+2YLaVPWBWGAMq7So6yQx+GG8YRMDeOEIf95Zn5RyiLMsEiX4KTNl9vq/w+NqRJkLA1kPIo15ufQ==", "dev": true, - "license": "MIT", "dependencies": { - "argparse": "^2.0.1", - "entities": "~3.0.1", - "linkify-it": "^4.0.1", - "mdurl": "^1.0.1", - "uc.micro": "^1.0.5" + "chalk": "5.3.0", + "commander": "11.1.0", + "debug": "4.3.4", + "execa": "8.0.1", + "lilconfig": "3.0.0", + "listr2": "8.0.0", + "micromatch": "4.0.5", + "pidtree": "0.6.0", + "string-argv": "0.3.2", + "yaml": "2.3.4" }, "bin": { - "markdown-it": "bin/markdown-it.js" - } - }, - "node_modules/markdown-it/node_modules/entities": { - "version": "3.0.1", - "dev": true, - "license": "BSD-2-Clause", + "lint-staged": "bin/lint-staged.js" + }, "engines": { - "node": ">=0.12" + "node": ">=18.12.0" }, "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" + "url": "https://opencollective.com/lint-staged" } }, - "node_modules/markdownlint": { - "version": "0.28.2", + "node_modules/lint-staged/node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", "dev": true, - "license": "MIT", "dependencies": { - "markdown-it": "13.0.1", - "markdownlint-micromark": "0.1.2" + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" }, "engines": { - "node": ">=14.18.0" + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/markdownlint-cli2": { - "version": "0.7.1", + "node_modules/lint-staged/node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", "dev": true, - "license": "MIT", - "dependencies": { - "globby": "13.1.4", - "markdownlint": "0.28.2", - "markdownlint-cli2-formatter-default": "0.0.4", - "micromatch": "4.0.5", - "strip-json-comments": "5.0.0", - "yaml": "2.2.2" - }, - "bin": { - "markdownlint-cli2": "markdownlint-cli2.js", - "markdownlint-cli2-config": "markdownlint-cli2-config.js", - "markdownlint-cli2-fix": "markdownlint-cli2-fix.js" - }, "engines": { - "node": ">=14.18.0" + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/markdownlint-cli2-formatter-default": { - "version": "0.0.4", + "node_modules/lint-staged/node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", "dev": true, - "license": "MIT", - "peerDependencies": { - "markdownlint-cli2": ">=0.0.4" + "engines": { + "node": ">=16.17.0" } }, - "node_modules/markdownlint-cli2/node_modules/globby": { - "version": "13.1.4", + "node_modules/lint-staged/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", "dev": true, - "license": "MIT", - "dependencies": { - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.11", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^4.0.0" - }, "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, @@ -7438,10 +9350,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/markdownlint-cli2/node_modules/slash": { + "node_modules/lint-staged/node_modules/mimic-fn": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", "dev": true, - "license": "MIT", "engines": { "node": ">=12" }, @@ -7449,7483 +9362,805 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/markdownlint-cli2/node_modules/strip-json-comments": { - "version": "5.0.0", + "node_modules/lint-staged/node_modules/npm-run-path": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.2.0.tgz", + "integrity": "sha512-W4/tgAXFqFA0iL7fk0+uQ3g7wkL8xJmx3XdK0VGb4cHW//eZTtKGvFBBoRKVTpY7n6ze4NL9ly7rgXcHufqXKg==", "dev": true, - "license": "MIT", + "dependencies": { + "path-key": "^4.0.0" + }, "engines": { - "node": ">=14.16" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/markdownlint-micromark": { - "version": "0.1.2", + "node_modules/lint-staged/node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", "dev": true, - "license": "MIT", + "dependencies": { + "mimic-fn": "^4.0.0" + }, "engines": { - "node": ">=14.18.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mdurl": { - "version": "1.0.1", + "node_modules/lint-staged/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", "dev": true, - "license": "MIT" - }, - "node_modules/media-typer": { - "version": "0.3.0", - "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/memory-cache": { - "version": "0.2.0", - "license": "BSD-2-Clause" - }, - "node_modules/merge-descriptors": { - "version": "1.0.1", - "license": "MIT" - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/merge2": { - "version": "1.4.1", + "node_modules/lint-staged/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, - "license": "MIT", "engines": { - "node": ">= 8" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/methods": { - "version": "1.1.2", - "license": "MIT", + "node_modules/lint-staged/node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, "engines": { - "node": ">= 0.6" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/micromatch": { - "version": "4.0.5", + "node_modules/listr2": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.0.0.tgz", + "integrity": "sha512-u8cusxAcyqAiQ2RhYvV7kRKNLgUvtObIbhOX2NCXqvp1UU32xIg5CT22ykS2TPKJXZWJwtK3IKLiqAGlGNE+Zg==", "dev": true, - "license": "MIT", "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" + "cli-truncate": "^4.0.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^6.0.0", + "rfdc": "^1.3.0", + "wrap-ansi": "^9.0.0" }, "engines": { - "node": ">=8.6" + "node": ">=18.0.0" } }, - "node_modules/mime": { - "version": "1.6.0", - "license": "MIT", - "bin": { - "mime": "cli.js" + "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": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mime-db": { - "version": "1.52.0", - "license": "MIT", + "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.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==" + }, + "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.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.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.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.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==", + "dev": true + }, + "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.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, + "node_modules/log-update": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.0.0.tgz", + "integrity": "sha512-niTvB4gqvtof056rRIrTZvjNYE4rCUzO6X/X+kYjd7WFxXeJ0NwEFnRxX6ehkvv3jTwrXnNdtAak5XYZuIyPFw==", + "dev": true, + "dependencies": { + "ansi-escapes": "^6.2.0", + "cli-cursor": "^4.0.0", + "slice-ansi": "^7.0.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, "engines": { - "node": ">= 0.6" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mime-types": { - "version": "2.1.35", - "license": "MIT", + "node_modules/log-update/node_modules/ansi-escapes": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.2.0.tgz", + "integrity": "sha512-kzRaCqXnpzWs+3z5ABPQiVke+iq0KXkHo8xiWV4RPTi5Yli0l97BEQuhXV1s7+aSU/fu1kUuxgS4MsQ0fRuygw==", + "dev": true, "dependencies": { - "mime-db": "1.52.0" + "type-fest": "^3.0.0" }, "engines": { - "node": ">= 0.6" + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mimic-fn": { - "version": "2.1.0", + "node_modules/log-update/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, - "license": "MIT", "engines": { - "node": ">=6" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/minimatch": { - "version": "3.1.2", - "license": "ISC", + "node_modules/log-update/node_modules/is-fullwidth-code-point": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz", + "integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==", + "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" + "get-east-asian-width": "^1.0.0" }, "engines": { - "node": "*" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/minimist": { - "version": "1.2.6", - "license": "MIT" - }, - "node_modules/mkdirp": { - "version": "1.0.4", + "node_modules/log-update/node_modules/slice-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz", + "integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==", "dev": true, - "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" + "dependencies": { + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" }, "engines": { - "node": ">=10" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, - "node_modules/module-details-from-path": { - "version": "1.0.3", - "license": "MIT" - }, - "node_modules/moment": { - "version": "2.29.4", - "license": "MIT", + "node_modules/log-update/node_modules/type-fest": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", + "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==", + "dev": true, "engines": { - "node": "*" + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/morgan": { - "version": "1.10.0", - "license": "MIT", - "dependencies": { - "basic-auth": "~2.0.1", - "debug": "2.6.9", - "depd": "~2.0.0", - "on-finished": "~2.3.0", - "on-headers": "~1.0.2" - }, + "node_modules/long": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" + }, + "node_modules/lru-cache": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz", + "integrity": "sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==", "engines": { - "node": ">= 0.8.0" + "node": "14 || >=16.14" } }, - "node_modules/morgan/node_modules/debug": { - "version": "2.6.9", - "license": "MIT", + "node_modules/lru-memoizer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-2.2.0.tgz", + "integrity": "sha512-QfOZ6jNkxCcM/BkIPnFsqDhtrazLRsghi9mBwFAzol5GCvj4EkFT899Za3+QwikCg5sRX8JstioBDwOxEyzaNw==", "dependencies": { - "ms": "2.0.0" + "lodash.clonedeep": "^4.5.0", + "lru-cache": "~4.0.0" } }, - "node_modules/morgan/node_modules/ms": { - "version": "2.0.0", - "license": "MIT" + "node_modules/lru-memoizer/node_modules/lru-cache": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.0.2.tgz", + "integrity": "sha512-uQw9OqphAGiZhkuPlpFGmdTU2tEuhxTourM/19qGJrxBPHAr/f8BT1a0i/lOclESnGatdJG/UCkP9kZB/Lh1iw==", + "dependencies": { + "pseudomap": "^1.0.1", + "yallist": "^2.0.0" + } }, - "node_modules/ms": { + "node_modules/lru-memoizer/node_modules/yallist": { "version": "2.1.2", - "license": "MIT" + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==" }, - "node_modules/mv": { - "version": "2.1.1", - "license": "MIT", - "optional": true, - "dependencies": { - "mkdirp": "~0.5.1", - "ncp": "~2.0.0", - "rimraf": "~2.4.0" - }, + "node_modules/luxon": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.4.4.tgz", + "integrity": "sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA==", "engines": { - "node": ">=0.8.0" + "node": ">=12" } }, - "node_modules/mv/node_modules/glob": { - "version": "6.0.4", - "license": "ISC", - "optional": true, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, "dependencies": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "2 || 3", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "semver": "^7.5.3" }, "engines": { - "node": "*" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mv/node_modules/mkdirp": { - "version": "0.5.6", - "license": "MIT", - "optional": true, + "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==", + "dev": true + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" + "tmpl": "1.0.5" } }, - "node_modules/mv/node_modules/rimraf": { - "version": "2.4.5", - "license": "ISC", - "optional": true, + "node_modules/markdown-it": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.0.0.tgz", + "integrity": "sha512-seFjF0FIcPt4P9U39Bq1JYblX0KZCjDLFFQPHpL5AzHpqPEKtosxmdq/LTVZnjfH7tjt9BxStm+wXcDBNuYmzw==", + "dev": true, "dependencies": { - "glob": "^6.0.1" + "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.0.0" }, "bin": { - "rimraf": "bin.js" + "markdown-it": "bin/markdown-it.mjs" } }, - "node_modules/nan": { - "version": "2.15.0", - "license": "MIT", - "optional": true - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "dev": true, - "license": "MIT" - }, - "node_modules/natural-compare-lite": { - "version": "1.4.0", + "node_modules/markdownlint": { + "version": "0.33.0", + "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.33.0.tgz", + "integrity": "sha512-4lbtT14A3m0LPX1WS/3d1m7Blg+ZwiLq36WvjQqFGsX3Gik99NV+VXp/PW3n+Q62xyPdbvGOCfjPqjW+/SKMig==", "dev": true, - "license": "MIT" - }, - "node_modules/ncp": { - "version": "2.0.0", - "license": "MIT", - "optional": true, - "bin": { - "ncp": "bin/ncp" - } - }, - "node_modules/negotiator": { - "version": "0.6.3", - "license": "MIT", + "dependencies": { + "markdown-it": "14.0.0", + "markdownlint-micromark": "0.1.8" + }, "engines": { - "node": ">= 0.6" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/DavidAnson" } }, - "node_modules/node-abort-controller": { - "version": "3.1.1", - "license": "MIT" - }, - "node_modules/node-fetch": { - "version": "2.6.11", - "license": "MIT", + "node_modules/markdownlint-cli2": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/markdownlint-cli2/-/markdownlint-cli2-0.12.0.tgz", + "integrity": "sha512-5nddNaPY1WC7BE1mkF8fiEkRDW8XbODwqYBPL3eyvFh7tLk74no+P8JRbCjghF7ozahCW5pF0TZ3ZS30m9H1Eg==", + "dev": true, "dependencies": { - "whatwg-url": "^5.0.0" + "globby": "14.0.0", + "markdownlint": "0.33.0", + "markdownlint-cli2-formatter-default": "0.0.4", + "micromatch": "4.0.5", + "strip-json-comments": "5.0.1", + "yaml": "2.3.4" }, - "engines": { - "node": "4.x || >=6.0.0" + "bin": { + "markdownlint-cli2": "markdownlint-cli2.js" }, - "peerDependencies": { - "encoding": "^0.1.0" + "engines": { + "node": ">=18" }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/DavidAnson" } }, - "node_modules/node-fetch/node_modules/tr46": { - "version": "0.0.3", - "license": "MIT" - }, - "node_modules/node-fetch/node_modules/webidl-conversions": { - "version": "3.0.1", - "license": "BSD-2-Clause" - }, - "node_modules/node-fetch/node_modules/whatwg-url": { - "version": "5.0.0", - "license": "MIT", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" + "node_modules/markdownlint-cli2-formatter-default": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/markdownlint-cli2-formatter-default/-/markdownlint-cli2-formatter-default-0.0.4.tgz", + "integrity": "sha512-xm2rM0E+sWgjpPn1EesPXx5hIyrN2ddUnUwnbCsD/ONxYtw3PX6LydvdH6dciWAoFDpwzbHM1TO7uHfcMd6IYg==", + "dev": true, + "peerDependencies": { + "markdownlint-cli2": ">=0.0.4" } }, - "node_modules/node-forge": { - "version": "1.3.1", - "license": "(BSD-3-Clause OR GPL-2.0)", - "engines": { - "node": ">= 6.13.0" - } - }, - "node_modules/node-int64": { - "version": "0.4.0", - "dev": true, - "license": "MIT" - }, - "node_modules/node-jose": { - "version": "2.2.0", - "license": "Apache-2.0", - "dependencies": { - "base64url": "^3.0.1", - "buffer": "^6.0.3", - "es6-promise": "^4.2.8", - "lodash": "^4.17.21", - "long": "^5.2.0", - "node-forge": "^1.2.1", - "pako": "^2.0.4", - "process": "^0.11.10", - "uuid": "^9.0.0" - } - }, - "node_modules/node-jose/node_modules/uuid": { - "version": "9.0.0", - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/node-releases": { - "version": "2.0.10", - "dev": true, - "license": "MIT" - }, - "node_modules/nodemailer": { - "version": "6.9.4", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.4.tgz", - "integrity": "sha512-CXjQvrQZV4+6X5wP6ZIgdehJamI63MFoYFGGPtHudWym9qaEHDNdPzaj5bfMCvxG1vhAileSWW90q7nL0N36mA==", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/oauth": { - "version": "0.9.15", - "license": "MIT" - }, - "node_modules/object-assign": { - "version": "4.1.1", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.12.3", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-path": { - "version": "0.11.8", - "license": "MIT", - "engines": { - "node": ">= 10.12.0" - } - }, - "node_modules/obuf": { - "version": "1.1.2", - "dev": true, - "license": "MIT" - }, - "node_modules/octicons": { - "version": "5.0.1", - "license": "(OFL-1.1 OR MIT)" - }, - "node_modules/on-finished": { - "version": "2.3.0", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/on-headers": { - "version": "1.0.2", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/open": { - "version": "8.4.0", - "license": "MIT", - "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.1", - "dev": true, - "license": "MIT", - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-map": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/packet-reader": { - "version": "1.0.0", - "license": "MIT" - }, - "node_modules/pako": { - "version": "2.0.4", - "license": "(MIT AND Zlib)" - }, - "node_modules/parent-module": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "callsites": "^3.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", + "node_modules/markdownlint-cli2/node_modules/globby": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-14.0.0.tgz", + "integrity": "sha512-/1WM/LNHRAOH9lZta77uGbq0dAEQM+XjNesWwhlERDVenqothRbnzTrL3/LrIoEPPjeUHC3vrS6TwoyxeHs7MQ==", "dev": true, - "license": "MIT", - "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/parseurl": { - "version": "1.3.3", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/passport": { - "version": "0.6.0", - "license": "MIT", "dependencies": { - "passport-strategy": "1.x.x", - "pause": "0.0.1", - "utils-merge": "^1.0.1" + "@sindresorhus/merge-streams": "^1.0.0", + "fast-glob": "^3.3.2", + "ignore": "^5.2.4", + "path-type": "^5.0.0", + "slash": "^5.1.0", + "unicorn-magic": "^0.1.0" }, "engines": { - "node": ">= 0.4.0" + "node": ">=18" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/jaredhanson" - } - }, - "node_modules/passport-azure-ad": { - "version": "4.3.5", - "license": "MIT", - "dependencies": { - "async": "^3.2.3", - "base64url": "^3.0.0", - "bunyan": "^1.8.14", - "cache-manager": "^3.6.1", - "https-proxy-agent": "^5.0.0", - "jws": "^3.1.3", - "lodash": "^4.11.2", - "node-jose": "^2.2.0", - "oauth": "0.9.15", - "passport": "^0.6.0", - "valid-url": "^1.0.6" - }, - "engines": { - "node": ">= 8.0.0" - } - }, - "node_modules/passport-azure-ad/node_modules/async": { - "version": "3.2.4", - "license": "MIT" - }, - "node_modules/passport-azure-ad/node_modules/jwa": { - "version": "1.4.1", - "license": "MIT", - "dependencies": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/passport-azure-ad/node_modules/jws": { - "version": "3.2.2", - "license": "MIT", - "dependencies": { - "jwa": "^1.4.1", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/passport-github": { - "version": "1.1.0", - "license": "MIT", - "dependencies": { - "passport-oauth2": "1.x.x" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/passport-oauth2": { - "version": "1.6.1", - "license": "MIT", - "dependencies": { - "base64url": "3.x.x", - "oauth": "0.9.x", - "passport-strategy": "1.x.x", - "uid2": "0.0.x", - "utils-merge": "1.x.x" - }, - "engines": { - "node": ">= 0.4.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/jaredhanson" - } - }, - "node_modules/passport-strategy": { - "version": "1.0.0", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/path-browserify": { - "version": "1.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/path-exists": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "license": "MIT" - }, - "node_modules/path-to-regexp": { - "version": "0.1.7", - "license": "MIT" - }, - "node_modules/path-type": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/pause": { - "version": "0.0.1" - }, - "node_modules/pg": { - "version": "8.11.0", - "license": "MIT", - "dependencies": { - "buffer-writer": "2.0.0", - "packet-reader": "1.0.0", - "pg-connection-string": "^2.6.0", - "pg-pool": "^3.6.0", - "pg-protocol": "^1.6.0", - "pg-types": "^2.1.0", - "pgpass": "1.x" - }, - "engines": { - "node": ">= 8.0.0" - }, - "optionalDependencies": { - "pg-cloudflare": "^1.1.0" - }, - "peerDependencies": { - "pg-native": ">=3.0.1" - }, - "peerDependenciesMeta": { - "pg-native": { - "optional": true - } - } - }, - "node_modules/pg-cloudflare": { - "version": "1.1.0", - "license": "MIT", - "optional": true - }, - "node_modules/pg-connection-string": { - "version": "2.6.0", - "license": "MIT" - }, - "node_modules/pg-escape": { - "version": "0.2.0", - "license": "MIT" - }, - "node_modules/pg-int8": { - "version": "1.0.1", - "license": "ISC", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/pg-numeric": { - "version": "1.0.2", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=4" - } - }, - "node_modules/pg-pool": { - "version": "3.6.0", - "license": "MIT", - "peerDependencies": { - "pg": ">=8.0" - } - }, - "node_modules/pg-protocol": { - "version": "1.6.0", - "license": "MIT" - }, - "node_modules/pg-types": { - "version": "2.2.0", - "license": "MIT", - "dependencies": { - "pg-int8": "1.0.1", - "postgres-array": "~2.0.0", - "postgres-bytea": "~1.0.0", - "postgres-date": "~1.0.4", - "postgres-interval": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/pgpass": { - "version": "1.0.5", - "license": "MIT", - "dependencies": { - "split2": "^4.1.0" - } - }, - "node_modules/picocolors": { - "version": "1.0.0", - "dev": true, - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pidtree": { - "version": "0.6.0", - "dev": true, - "license": "MIT", - "bin": { - "pidtree": "bin/pidtree.js" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/pirates": { - "version": "4.0.5", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/find-up": { - "version": "4.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/locate-path": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/p-limit": { - "version": "2.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pkg-dir/node_modules/p-locate": { - "version": "4.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/postgres-array": { - "version": "2.0.0", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/postgres-bytea": { - "version": "1.0.0", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postgres-date": { - "version": "1.0.7", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postgres-interval": { - "version": "1.2.0", - "license": "MIT", - "dependencies": { - "xtend": "^4.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postgres-range": { - "version": "1.1.3", - "dev": true, - "license": "MIT" - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prettier": { - "version": "2.8.8", - "dev": true, - "license": "MIT", - "bin": { - "prettier": "bin-prettier.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/prettier-linter-helpers": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-diff": "^1.1.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/pretty-format": { - "version": "29.5.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.4.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/priorityqueuejs": { - "version": "1.0.0", - "license": "MIT" - }, - "node_modules/process": { - "version": "0.11.10", - "license": "MIT", - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/promise": { - "version": "7.3.1", - "license": "MIT", - "dependencies": { - "asap": "~2.0.3" - } - }, - "node_modules/prompts": { - "version": "2.4.2", - "dev": true, - "license": "MIT", - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "license": "MIT", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "license": "MIT" - }, - "node_modules/pseudomap": { - "version": "1.0.2", - "license": "ISC" - }, - "node_modules/pug": { - "version": "3.0.2", - "license": "MIT", - "dependencies": { - "pug-code-gen": "^3.0.2", - "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", - "license": "MIT", - "dependencies": { - "constantinople": "^4.0.1", - "js-stringify": "^1.0.2", - "pug-runtime": "^3.0.0" - } - }, - "node_modules/pug-code-gen": { - "version": "3.0.2", - "license": "MIT", - "dependencies": { - "constantinople": "^4.0.1", - "doctypes": "^1.1.0", - "js-stringify": "^1.0.2", - "pug-attrs": "^3.0.0", - "pug-error": "^2.0.0", - "pug-runtime": "^3.0.0", - "void-elements": "^3.1.0", - "with": "^7.0.0" - } - }, - "node_modules/pug-error": { - "version": "2.0.0", - "license": "MIT" - }, - "node_modules/pug-filters": { - "version": "4.0.0", - "license": "MIT", - "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", - "license": "MIT", - "dependencies": { - "character-parser": "^2.2.0", - "is-expression": "^4.0.0", - "pug-error": "^2.0.0" - } - }, - "node_modules/pug-linker": { - "version": "4.0.0", - "license": "MIT", - "dependencies": { - "pug-error": "^2.0.0", - "pug-walk": "^2.0.0" - } - }, - "node_modules/pug-load": { - "version": "3.0.0", - "license": "MIT", - "dependencies": { - "object-assign": "^4.1.1", - "pug-walk": "^2.0.0" - } - }, - "node_modules/pug-parser": { - "version": "6.0.0", - "license": "MIT", - "dependencies": { - "pug-error": "^2.0.0", - "token-stream": "1.0.0" - } - }, - "node_modules/pug-runtime": { - "version": "3.0.1", - "license": "MIT" - }, - "node_modules/pug-strip-comments": { - "version": "2.0.0", - "license": "MIT", - "dependencies": { - "pug-error": "^2.0.0" - } - }, - "node_modules/pug-walk": { - "version": "2.0.0", - "license": "MIT" - }, - "node_modules/punycode": { - "version": "2.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/pure-rand": { - "version": "6.0.2", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ], - "license": "MIT" - }, - "node_modules/qs": { - "version": "6.11.0", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "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" - } - ], - "license": "MIT" - }, - "node_modules/random-bytes": { - "version": "1.0.0", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.5.2", - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/react-is": { - "version": "18.2.0", - "dev": true, - "license": "MIT" - }, - "node_modules/recursive-readdir": { - "version": "2.2.3", - "license": "MIT", - "dependencies": { - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/redis": { - "version": "4.6.8", - "resolved": "https://registry.npmjs.org/redis/-/redis-4.6.8.tgz", - "integrity": "sha512-S7qNkPUYrsofQ0ztWlTHSaK0Qqfl1y+WMIxrzeAGNG+9iUZB4HGeBgkHxE6uJJ6iXrkvLd1RVJ2nvu6H1sAzfQ==", - "dependencies": { - "@redis/bloom": "1.2.0", - "@redis/client": "1.5.9", - "@redis/graph": "1.1.0", - "@redis/json": "1.0.4", - "@redis/search": "1.1.3", - "@redis/time-series": "1.0.5" - } - }, - "node_modules/repeat-string": { - "version": "1.6.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-in-the-middle": { - "version": "5.2.0", - "license": "MIT", - "dependencies": { - "debug": "^4.1.1", - "module-details-from-path": "^1.0.3", - "resolve": "^1.22.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/resolve": { - "version": "1.22.2", - "license": "MIT", - "dependencies": { - "is-core-module": "^2.11.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/resolve-cwd": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-from": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-global": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "global-dirs": "^0.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve.exports": { - "version": "2.0.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/restore-cursor": { - "version": "3.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "dev": true, - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rfdc": { - "version": "1.3.0", - "dev": true, - "license": "MIT" - }, - "node_modules/rhea": { - "version": "3.0.2", - "license": "Apache-2.0", - "dependencies": { - "debug": "^4.3.3" - } - }, - "node_modules/rhea-promise": { - "version": "3.0.1", - "license": "Apache-2.0", - "dependencies": { - "debug": "^3.1.0", - "rhea": "^3.0.0", - "tslib": "^2.2.0" - } - }, - "node_modules/rhea-promise/node_modules/debug": { - "version": "3.2.7", - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "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" - } - ], - "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/rxjs": { - "version": "7.8.1", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/safe-buffer": { - "version": "5.1.2", - "license": "MIT" - }, - "node_modules/safe-json-stringify": { - "version": "1.2.0", - "license": "MIT", - "optional": true - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "license": "MIT" - }, - "node_modules/sax": { - "version": "1.2.4", - "license": "ISC" - }, - "node_modules/secure-compare": { - "version": "3.0.1", - "license": "MIT" - }, - "node_modules/semaphore": { - "version": "1.1.0", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/semver": { - "version": "7.5.1", - "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/send": { - "version": "0.18.0", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/debug": { - "version": "2.6.9", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/send/node_modules/debug/node_modules/ms": { - "version": "2.0.0", - "license": "MIT" - }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "license": "MIT" - }, - "node_modules/send/node_modules/on-finished": { - "version": "2.4.1", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/serve-favicon": { - "version": "2.5.0", - "license": "MIT", - "dependencies": { - "etag": "~1.8.1", - "fresh": "0.5.2", - "ms": "2.1.1", - "parseurl": "~1.3.2", - "safe-buffer": "5.1.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/serve-favicon/node_modules/ms": { - "version": "2.1.1", - "license": "MIT" - }, - "node_modules/serve-favicon/node_modules/safe-buffer": { - "version": "5.1.1", - "license": "MIT" - }, - "node_modules/serve-static": { - "version": "1.15.0", - "license": "MIT", - "dependencies": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.18.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "license": "ISC" - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/shimmer": { - "version": "1.2.1", - "license": "BSD-2-Clause" - }, - "node_modules/side-channel": { - "version": "1.0.4", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "dev": true, - "license": "ISC" - }, - "node_modules/simple-oauth2": { - "version": "5.0.0", - "license": "Apache-2.0", - "dependencies": { - "@hapi/hoek": "^10.0.1", - "@hapi/wreck": "^18.0.0", - "debug": "^4.3.4", - "joi": "^17.6.4" - } - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "dev": true, - "license": "MIT" - }, - "node_modules/slash": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/slice-ansi": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.0.0", - "is-fullwidth-code-point": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/slice-ansi/node_modules/ansi-styles": { - "version": "6.2.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/slice-ansi/node_modules/is-fullwidth-code-point": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.13", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/split2": { - "version": "4.1.0", - "license": "ISC", - "engines": { - "node": ">= 10.x" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/stack-chain": { - "version": "1.3.7", - "license": "MIT" - }, - "node_modules/stack-utils": { - "version": "2.0.6", - "dev": true, - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/stack-utils/node_modules/escape-string-regexp": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/statuses": { - "version": "2.0.1", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/stoppable": { - "version": "1.1.0", - "license": "MIT", - "engines": { - "node": ">=4", - "npm": ">=6" - } - }, - "node_modules/string-argv": { - "version": "0.3.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.6.19" - } - }, - "node_modules/string-length": { - "version": "4.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "4.0.0", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/strnum": { - "version": "1.0.5", - "license": "MIT" - }, - "node_modules/supports-color": { - "version": "7.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/test-exclude": { - "version": "6.0.0", - "dev": true, - "license": "ISC", - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/text-table": { - "version": "0.2.0", - "dev": true, - "license": "MIT" - }, - "node_modules/throat": { - "version": "6.0.2", - "license": "MIT" - }, - "node_modules/through": { - "version": "2.3.8", - "dev": true, - "license": "MIT" - }, - "node_modules/tmp": { - "version": "0.2.1", - "license": "MIT", - "dependencies": { - "rimraf": "^3.0.0" - }, - "engines": { - "node": ">=8.17.0" - } - }, - "node_modules/tmp-promise": { - "version": "3.0.3", - "license": "MIT", - "dependencies": { - "tmp": "^0.2.0" - } - }, - "node_modules/tmpl": { - "version": "1.0.5", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/token-stream": { - "version": "1.0.0", - "license": "MIT" - }, - "node_modules/true-myth": { - "version": "4.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": "10.* || >= 12.*" - } - }, - "node_modules/ts-jest": { - "version": "29.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "bs-logger": "0.x", - "fast-json-stable-stringify": "2.x", - "jest-util": "^29.0.0", - "json5": "^2.2.3", - "lodash.memoize": "4.x", - "make-error": "1.x", - "semver": "7.x", - "yargs-parser": "^21.0.1" - }, - "bin": { - "ts-jest": "cli.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": ">=7.0.0-beta.0 <8", - "@jest/types": "^29.0.0", - "babel-jest": "^29.0.0", - "jest": "^29.0.0", - "typescript": ">=4.3 <6" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "@jest/types": { - "optional": true - }, - "babel-jest": { - "optional": true - }, - "esbuild": { - "optional": true - } - } - }, - "node_modules/ts-morph": { - "version": "13.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@ts-morph/common": "~0.12.3", - "code-block-writer": "^11.0.0" - } - }, - "node_modules/ts-node": { - "version": "10.9.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } - } - }, - "node_modules/ts-prune": { - "version": "0.10.3", - "dev": true, - "license": "MIT", - "dependencies": { - "commander": "^6.2.1", - "cosmiconfig": "^7.0.1", - "json5": "^2.1.3", - "lodash": "^4.17.21", - "true-myth": "^4.1.0", - "ts-morph": "^13.0.1" - }, - "bin": { - "ts-prune": "lib/index.js" - } - }, - "node_modules/ts-prune/node_modules/commander": { - "version": "6.2.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/ts-prune/node_modules/cosmiconfig": { - "version": "7.1.0", - "dev": true, - "license": "MIT", - "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/ts-prune/node_modules/yaml": { - "version": "1.10.2", - "dev": true, - "license": "ISC", - "engines": { - "node": ">= 6" - } - }, - "node_modules/tslib": { - "version": "2.3.1", - "license": "0BSD" - }, - "node_modules/tsutils": { - "version": "3.21.0", - "dev": true, - "license": "MIT", - "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", - "dev": true, - "license": "0BSD" - }, - "node_modules/tunnel": { - "version": "0.0.6", - "license": "MIT", - "engines": { - "node": ">=0.6.11 <=0.7.0 || >=0.7.3" - } - }, - "node_modules/type-check": { - "version": "0.4.0", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-detect": { - "version": "4.0.8", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/type-fest": { - "version": "0.20.2", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/type-is": { - "version": "1.6.18", - "license": "MIT", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/typedarray-to-buffer": { - "version": "3.1.5", - "dev": true, - "license": "MIT", - "dependencies": { - "is-typedarray": "^1.0.0" - } - }, - "node_modules/typescript": { - "version": "5.0.4", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=12.20" - } - }, - "node_modules/uc.micro": { - "version": "1.0.6", - "dev": true, - "license": "MIT" - }, - "node_modules/uid-safe": { - "version": "2.1.5", - "license": "MIT", - "dependencies": { - "random-bytes": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/uid2": { - "version": "0.0.4", - "license": "MIT" - }, - "node_modules/unique-string": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "crypto-random-string": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/universal-github-app-jwt": { - "version": "1.1.1", - "license": "MIT", - "dependencies": { - "@types/jsonwebtoken": "^9.0.0", - "jsonwebtoken": "^9.0.0" - } - }, - "node_modules/universal-user-agent": { - "version": "6.0.0", - "license": "ISC" - }, - "node_modules/unpipe": { - "version": "1.0.0", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.0.11", - "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" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/util": { - "version": "0.12.5", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "is-arguments": "^1.0.4", - "is-generator-function": "^1.0.7", - "is-typed-array": "^1.1.3", - "which-typed-array": "^1.1.2" - } - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "license": "MIT", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/uuid": { - "version": "8.3.2", - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/v8-to-istanbul": { - "version": "9.1.0", - "dev": true, - "license": "ISC", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/v8-to-istanbul/node_modules/convert-source-map": { - "version": "1.9.0", - "dev": true, - "license": "MIT" - }, - "node_modules/valid-url": { - "version": "1.0.9" - }, - "node_modules/validator": { - "version": "13.9.0", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/void-elements": { - "version": "3.1.0", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/vscode-languageserver-textdocument": { - "version": "1.0.8", - "dev": true, - "license": "MIT" - }, - "node_modules/vscode-uri": { - "version": "3.0.7", - "dev": true, - "license": "MIT" - }, - "node_modules/walk-back": { - "version": "5.1.0", - "license": "MIT", - "engines": { - "node": ">=12.17" - } - }, - "node_modules/walker": { - "version": "1.0.8", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "makeerror": "1.0.12" - } - }, - "node_modules/which": { - "version": "2.0.2", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/which-typed-array": { - "version": "1.1.9", - "license": "MIT", - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.10" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/with": { - "version": "7.0.2", - "license": "MIT", - "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.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "license": "ISC" - }, - "node_modules/write-file-atomic": { - "version": "4.0.2", - "dev": true, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/xdg-basedir": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/xml": { - "version": "1.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/xmlbuilder": { - "version": "11.0.1", - "license": "MIT", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/xtend": { - "version": "4.0.2", - "license": "MIT", - "engines": { - "node": ">=0.4" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "4.0.0", - "license": "ISC" - }, - "node_modules/yaml": { - "version": "2.2.2", - "dev": true, - "license": "ISC", - "engines": { - "node": ">= 14" - } - }, - "node_modules/yargs": { - "version": "17.7.2", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/yn": { - "version": "3.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - }, - "dependencies": { - "@ampproject/remapping": { - "version": "2.2.1", - "dev": true, - "requires": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "@azure/abort-controller": { - "version": "1.0.4", - "requires": { - "tslib": "^2.0.0" - } - }, - "@azure/core-amqp": { - "version": "3.3.0", - "requires": { - "@azure/abort-controller": "^1.0.0", - "@azure/core-auth": "^1.3.0", - "@azure/core-util": "^1.1.1", - "@azure/logger": "^1.0.0", - "buffer": "^6.0.0", - "events": "^3.0.0", - "jssha": "^3.1.0", - "process": "^0.11.10", - "rhea": "^3.0.0", - "rhea-promise": "^3.0.0", - "tslib": "^2.2.0", - "util": "^0.12.1" - }, - "dependencies": { - "@azure/core-util": { - "version": "1.3.2", - "requires": { - "@azure/abort-controller": "^1.0.0", - "tslib": "^2.2.0" - } - } - } - }, - "@azure/core-asynciterator-polyfill": { - "version": "1.0.2" - }, - "@azure/core-auth": { - "version": "1.4.0", - "requires": { - "@azure/abort-controller": "^1.0.0", - "tslib": "^2.2.0" - } - }, - "@azure/core-client": { - "version": "1.5.0", - "requires": { - "@azure/abort-controller": "^1.0.0", - "@azure/core-asynciterator-polyfill": "^1.0.0", - "@azure/core-auth": "^1.3.0", - "@azure/core-rest-pipeline": "^1.5.0", - "@azure/core-tracing": "1.0.0-preview.13", - "@azure/logger": "^1.0.0", - "tslib": "^2.2.0" - } - }, - "@azure/core-http-compat": { - "version": "1.3.0", - "requires": { - "@azure/abort-controller": "^1.0.4", - "@azure/core-client": "^1.3.0", - "@azure/core-rest-pipeline": "^1.3.0" - } - }, - "@azure/core-lro": { - "version": "2.2.4", - "requires": { - "@azure/abort-controller": "^1.0.0", - "@azure/core-tracing": "1.0.0-preview.13", - "@azure/logger": "^1.0.0", - "tslib": "^2.2.0" - } - }, - "@azure/core-paging": { - "version": "1.5.0", - "requires": { - "tslib": "^2.2.0" - } - }, - "@azure/core-rest-pipeline": { - "version": "1.10.1", - "requires": { - "@azure/abort-controller": "^1.0.0", - "@azure/core-auth": "^1.4.0", - "@azure/core-tracing": "^1.0.1", - "@azure/core-util": "^1.0.0", - "@azure/logger": "^1.0.0", - "form-data": "^4.0.0", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", - "tslib": "^2.2.0", - "uuid": "^8.3.0" - }, - "dependencies": { - "@azure/core-tracing": { - "version": "1.0.1", - "requires": { - "tslib": "^2.2.0" - } - } - } - }, - "@azure/core-tracing": { - "version": "1.0.0-preview.13", - "requires": { - "@opentelemetry/api": "^1.0.1", - "tslib": "^2.2.0" - } - }, - "@azure/core-util": { - "version": "1.2.0", - "requires": { - "@azure/abort-controller": "^1.0.0", - "tslib": "^2.2.0" - } - }, - "@azure/core-xml": { - "version": "1.3.3", - "requires": { - "fast-xml-parser": "^4.0.8", - "tslib": "^2.2.0" - } - }, - "@azure/cosmos": { - "version": "3.17.3", - "requires": { - "@azure/abort-controller": "^1.0.0", - "@azure/core-auth": "^1.3.0", - "@azure/core-rest-pipeline": "^1.2.0", - "@azure/core-tracing": "^1.0.0", - "debug": "^4.1.1", - "fast-json-stable-stringify": "^2.1.0", - "jsbi": "^3.1.3", - "node-abort-controller": "^3.0.0", - "priorityqueuejs": "^1.0.0", - "semaphore": "^1.0.5", - "tslib": "^2.2.0", - "universal-user-agent": "^6.0.0", - "uuid": "^8.3.0" - }, - "dependencies": { - "@azure/core-tracing": { - "version": "1.0.1", - "requires": { - "tslib": "^2.2.0" - } - } - } - }, - "@azure/data-tables": { - "version": "13.2.2", - "requires": { - "@azure/core-auth": "^1.3.0", - "@azure/core-client": "^1.0.0", - "@azure/core-paging": "^1.1.1", - "@azure/core-rest-pipeline": "^1.1.0", - "@azure/core-tracing": "^1.0.0", - "@azure/core-xml": "^1.0.0", - "@azure/logger": "^1.0.0", - "tslib": "^2.2.0", - "uuid": "^8.3.0" - }, - "dependencies": { - "@azure/core-tracing": { - "version": "1.0.1", - "requires": { - "tslib": "^2.2.0" - } - } - } - }, - "@azure/identity": { - "version": "3.2.2", - "requires": { - "@azure/abort-controller": "^1.0.0", - "@azure/core-auth": "^1.3.0", - "@azure/core-client": "^1.4.0", - "@azure/core-rest-pipeline": "^1.1.0", - "@azure/core-tracing": "^1.0.0", - "@azure/core-util": "^1.0.0", - "@azure/logger": "^1.0.0", - "@azure/msal-browser": "^2.32.2", - "@azure/msal-common": "^9.0.2", - "@azure/msal-node": "^1.14.6", - "events": "^3.0.0", - "jws": "^4.0.0", - "open": "^8.0.0", - "stoppable": "^1.1.0", - "tslib": "^2.2.0", - "uuid": "^8.3.0" - }, - "dependencies": { - "@azure/core-tracing": { - "version": "1.0.1", - "requires": { - "tslib": "^2.2.0" - } - } - } - }, - "@azure/keyvault-secrets": { - "version": "4.7.0", - "requires": { - "@azure/abort-controller": "^1.0.0", - "@azure/core-auth": "^1.3.0", - "@azure/core-client": "^1.5.0", - "@azure/core-http-compat": "^1.3.0", - "@azure/core-lro": "^2.2.0", - "@azure/core-paging": "^1.1.1", - "@azure/core-rest-pipeline": "^1.8.0", - "@azure/core-tracing": "^1.0.0", - "@azure/core-util": "^1.0.0", - "@azure/logger": "^1.0.0", - "tslib": "^2.2.0" - }, - "dependencies": { - "@azure/core-tracing": { - "version": "1.0.1", - "requires": { - "tslib": "^2.2.0" - } - } - } - }, - "@azure/logger": { - "version": "1.0.3", - "requires": { - "tslib": "^2.2.0" - } - }, - "@azure/msal-browser": { - "version": "2.37.0", - "requires": { - "@azure/msal-common": "13.0.0" - }, - "dependencies": { - "@azure/msal-common": { - "version": "13.0.0" - } - } - }, - "@azure/msal-common": { - "version": "9.1.1" - }, - "@azure/msal-node": { - "version": "1.17.2", - "requires": { - "@azure/msal-common": "13.0.0", - "jsonwebtoken": "^9.0.0", - "uuid": "^8.3.0" - }, - "dependencies": { - "@azure/msal-common": { - "version": "13.0.0" - } - } - }, - "@azure/opentelemetry-instrumentation-azure-sdk": { - "version": "1.0.0-beta.3", - "requires": { - "@azure/core-tracing": "^1.0.0", - "@azure/logger": "^1.0.0", - "@opentelemetry/api": "^1.4.0", - "@opentelemetry/core": "^1.9.0", - "@opentelemetry/instrumentation": "^0.35.0", - "tslib": "^2.2.0" - }, - "dependencies": { - "@azure/core-tracing": { - "version": "1.0.1", - "requires": { - "tslib": "^2.2.0" - } - } - } - }, - "@azure/service-bus": { - "version": "7.9.0", - "requires": { - "@azure/abort-controller": "^1.0.0", - "@azure/core-amqp": "^3.3.0", - "@azure/core-auth": "^1.3.0", - "@azure/core-client": "^1.0.0", - "@azure/core-paging": "^1.4.0", - "@azure/core-rest-pipeline": "^1.1.0", - "@azure/core-tracing": "^1.0.0", - "@azure/core-util": "^1.1.1", - "@azure/core-xml": "^1.0.0", - "@azure/logger": "^1.0.0", - "@types/is-buffer": "^2.0.0", - "buffer": "^6.0.0", - "is-buffer": "^2.0.3", - "jssha": "^3.1.0", - "long": "^5.2.0", - "process": "^0.11.10", - "rhea-promise": "^3.0.0", - "tslib": "^2.2.0" - }, - "dependencies": { - "@azure/core-tracing": { - "version": "1.0.1", - "requires": { - "tslib": "^2.2.0" - } - }, - "@azure/core-util": { - "version": "1.3.2", - "requires": { - "@azure/abort-controller": "^1.0.0", - "tslib": "^2.2.0" - } - } - } - }, - "@azure/storage-blob": { - "version": "12.14.0", - "requires": { - "@azure/abort-controller": "^1.0.0", - "@azure/core-http": "^3.0.0", - "@azure/core-lro": "^2.2.0", - "@azure/core-paging": "^1.1.1", - "@azure/core-tracing": "1.0.0-preview.13", - "@azure/logger": "^1.0.0", - "events": "^3.0.0", - "tslib": "^2.2.0" - }, - "dependencies": { - "@azure/core-http": { - "version": "3.0.1", - "requires": { - "@azure/abort-controller": "^1.0.0", - "@azure/core-auth": "^1.3.0", - "@azure/core-tracing": "1.0.0-preview.13", - "@azure/core-util": "^1.1.1", - "@azure/logger": "^1.0.0", - "@types/node-fetch": "^2.5.0", - "@types/tunnel": "^0.0.3", - "form-data": "^4.0.0", - "node-fetch": "^2.6.7", - "process": "^0.11.10", - "tslib": "^2.2.0", - "tunnel": "^0.0.6", - "uuid": "^8.3.0", - "xml2js": "^0.5.0" - } - }, - "xml2js": { - "version": "0.5.0", - "requires": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - } - } - } - }, - "@azure/storage-queue": { - "version": "12.14.0", - "resolved": "https://registry.npmjs.org/@azure/storage-queue/-/storage-queue-12.14.0.tgz", - "integrity": "sha512-1j6uxhzCcbEDVPOTNWIJ5CsLzOAU5U/bXgGZeT25fy6IghFTC1JlPGALez2CWJ9fBVj6AmSnsiBXL/77iXhSpg==", - "requires": { - "@azure/abort-controller": "^1.0.0", - "@azure/core-http": "^3.0.0", - "@azure/core-paging": "^1.1.1", - "@azure/core-tracing": "1.0.0-preview.13", - "@azure/logger": "^1.0.0", - "tslib": "^2.2.0" - }, - "dependencies": { - "@azure/core-http": { - "version": "3.0.1", - "requires": { - "@azure/abort-controller": "^1.0.0", - "@azure/core-auth": "^1.3.0", - "@azure/core-tracing": "1.0.0-preview.13", - "@azure/core-util": "^1.1.1", - "@azure/logger": "^1.0.0", - "@types/node-fetch": "^2.5.0", - "@types/tunnel": "^0.0.3", - "form-data": "^4.0.0", - "node-fetch": "^2.6.7", - "process": "^0.11.10", - "tslib": "^2.2.0", - "tunnel": "^0.0.6", - "uuid": "^8.3.0", - "xml2js": "^0.5.0" - } - }, - "xml2js": { - "version": "0.5.0", - "requires": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - } - } - } - }, - "@babel/code-frame": { - "version": "7.21.4", - "dev": true, - "requires": { - "@babel/highlight": "^7.18.6" - } - }, - "@babel/compat-data": { - "version": "7.21.7", - "dev": true - }, - "@babel/core": { - "version": "7.21.8", - "dev": true, - "requires": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.21.4", - "@babel/generator": "^7.21.5", - "@babel/helper-compilation-targets": "^7.21.5", - "@babel/helper-module-transforms": "^7.21.5", - "@babel/helpers": "^7.21.5", - "@babel/parser": "^7.21.8", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.5", - "@babel/types": "^7.21.5", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", - "semver": "^6.3.0" - }, - "dependencies": { - "convert-source-map": { - "version": "1.9.0", - "dev": true - }, - "semver": { - "version": "6.3.0", - "dev": true - } - } - }, - "@babel/generator": { - "version": "7.21.5", - "dev": true, - "requires": { - "@babel/types": "^7.21.5", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" - } - }, - "@babel/helper-compilation-targets": { - "version": "7.21.5", - "dev": true, - "requires": { - "@babel/compat-data": "^7.21.5", - "@babel/helper-validator-option": "^7.21.0", - "browserslist": "^4.21.3", - "lru-cache": "^5.1.1", - "semver": "^6.3.0" - }, - "dependencies": { - "lru-cache": { - "version": "5.1.1", - "dev": true, - "requires": { - "yallist": "^3.0.2" - } - }, - "semver": { - "version": "6.3.0", - "dev": true - }, - "yallist": { - "version": "3.1.1", - "dev": true - } - } - }, - "@babel/helper-environment-visitor": { - "version": "7.21.5", - "dev": true - }, - "@babel/helper-function-name": { - "version": "7.21.0", - "dev": true, - "requires": { - "@babel/template": "^7.20.7", - "@babel/types": "^7.21.0" - } - }, - "@babel/helper-hoist-variables": { - "version": "7.18.6", - "dev": true, - "requires": { - "@babel/types": "^7.18.6" - } - }, - "@babel/helper-module-imports": { - "version": "7.21.4", - "dev": true, - "requires": { - "@babel/types": "^7.21.4" - } - }, - "@babel/helper-module-transforms": { - "version": "7.21.5", - "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.21.5", - "@babel/helper-module-imports": "^7.21.4", - "@babel/helper-simple-access": "^7.21.5", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.19.1", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.5", - "@babel/types": "^7.21.5" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.21.5", - "dev": true - }, - "@babel/helper-simple-access": { - "version": "7.21.5", - "dev": true, - "requires": { - "@babel/types": "^7.21.5" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.18.6", - "dev": true, - "requires": { - "@babel/types": "^7.18.6" - } - }, - "@babel/helper-string-parser": { - "version": "7.21.5" - }, - "@babel/helper-validator-identifier": { - "version": "7.19.1" - }, - "@babel/helper-validator-option": { - "version": "7.21.0", - "dev": true - }, - "@babel/helpers": { - "version": "7.21.5", - "dev": true, - "requires": { - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.5", - "@babel/types": "^7.21.5" - } - }, - "@babel/highlight": { - "version": "7.18.6", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@babel/parser": { - "version": "7.21.8" - }, - "@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-jsx": { - "version": "7.21.4", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.20.2" - } - }, - "@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-typescript": { - "version": "7.21.4", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.20.2" - } - }, - "@babel/template": { - "version": "7.20.7", - "dev": true, - "requires": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7" - } - }, - "@babel/traverse": { - "version": "7.21.5", - "dev": true, - "requires": { - "@babel/code-frame": "^7.21.4", - "@babel/generator": "^7.21.5", - "@babel/helper-environment-visitor": "^7.21.5", - "@babel/helper-function-name": "^7.21.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.21.5", - "@babel/types": "^7.21.5", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, - "dependencies": { - "globals": { - "version": "11.12.0", - "dev": true - } - } - }, - "@babel/types": { - "version": "7.21.5", - "requires": { - "@babel/helper-string-parser": "^7.21.5", - "@babel/helper-validator-identifier": "^7.19.1", - "to-fast-properties": "^2.0.0" - } - }, - "@bcoe/v8-coverage": { - "version": "0.2.3", - "dev": true - }, - "@cspell/cspell-bundled-dicts": { - "version": "6.31.1", - "dev": true, - "requires": { - "@cspell/dict-ada": "^4.0.1", - "@cspell/dict-aws": "^3.0.0", - "@cspell/dict-bash": "^4.1.1", - "@cspell/dict-companies": "^3.0.9", - "@cspell/dict-cpp": "^5.0.2", - "@cspell/dict-cryptocurrencies": "^3.0.1", - "@cspell/dict-csharp": "^4.0.2", - "@cspell/dict-css": "^4.0.5", - "@cspell/dict-dart": "^2.0.2", - "@cspell/dict-django": "^4.0.2", - "@cspell/dict-docker": "^1.1.6", - "@cspell/dict-dotnet": "^5.0.0", - "@cspell/dict-elixir": "^4.0.2", - "@cspell/dict-en_us": "^4.3.2", - "@cspell/dict-en-common-misspellings": "^1.0.2", - "@cspell/dict-en-gb": "1.1.33", - "@cspell/dict-filetypes": "^3.0.0", - "@cspell/dict-fonts": "^3.0.1", - "@cspell/dict-fullstack": "^3.1.5", - "@cspell/dict-gaming-terms": "^1.0.4", - "@cspell/dict-git": "^2.0.0", - "@cspell/dict-golang": "^6.0.1", - "@cspell/dict-haskell": "^4.0.1", - "@cspell/dict-html": "^4.0.3", - "@cspell/dict-html-symbol-entities": "^4.0.0", - "@cspell/dict-java": "^5.0.5", - "@cspell/dict-k8s": "^1.0.1", - "@cspell/dict-latex": "^4.0.0", - "@cspell/dict-lorem-ipsum": "^3.0.0", - "@cspell/dict-lua": "^4.0.1", - "@cspell/dict-node": "^4.0.2", - "@cspell/dict-npm": "^5.0.5", - "@cspell/dict-php": "^4.0.1", - "@cspell/dict-powershell": "^5.0.1", - "@cspell/dict-public-licenses": "^2.0.2", - "@cspell/dict-python": "^4.0.2", - "@cspell/dict-r": "^2.0.1", - "@cspell/dict-ruby": "^5.0.0", - "@cspell/dict-rust": "^4.0.1", - "@cspell/dict-scala": "^5.0.0", - "@cspell/dict-software-terms": "^3.1.6", - "@cspell/dict-sql": "^2.1.0", - "@cspell/dict-svelte": "^1.0.2", - "@cspell/dict-swift": "^2.0.1", - "@cspell/dict-typescript": "^3.1.1", - "@cspell/dict-vue": "^3.0.0" - } - }, - "@cspell/cspell-pipe": { - "version": "6.31.1", - "dev": true - }, - "@cspell/cspell-service-bus": { - "version": "6.31.1", - "dev": true - }, - "@cspell/cspell-types": { - "version": "6.31.1", - "dev": true - }, - "@cspell/dict-ada": { - "version": "4.0.1", - "dev": true - }, - "@cspell/dict-aws": { - "version": "3.0.0", - "dev": true - }, - "@cspell/dict-bash": { - "version": "4.1.1", - "dev": true - }, - "@cspell/dict-companies": { - "version": "3.0.10", - "dev": true - }, - "@cspell/dict-cpp": { - "version": "5.0.3", - "dev": true - }, - "@cspell/dict-cryptocurrencies": { - "version": "3.0.1", - "dev": true - }, - "@cspell/dict-csharp": { - "version": "4.0.2", - "dev": true - }, - "@cspell/dict-css": { - "version": "4.0.6", - "dev": true - }, - "@cspell/dict-dart": { - "version": "2.0.2", - "dev": true - }, - "@cspell/dict-django": { - "version": "4.0.2", - "dev": true - }, - "@cspell/dict-docker": { - "version": "1.1.6", - "dev": true - }, - "@cspell/dict-dotnet": { - "version": "5.0.0", - "dev": true - }, - "@cspell/dict-elixir": { - "version": "4.0.3", - "dev": true - }, - "@cspell/dict-en_us": { - "version": "4.3.2", - "dev": true - }, - "@cspell/dict-en-common-misspellings": { - "version": "1.0.2", - "dev": true - }, - "@cspell/dict-en-gb": { - "version": "1.1.33", - "dev": true - }, - "@cspell/dict-filetypes": { - "version": "3.0.0", - "dev": true - }, - "@cspell/dict-fonts": { - "version": "3.0.2", - "dev": true - }, - "@cspell/dict-fullstack": { - "version": "3.1.5", - "dev": true - }, - "@cspell/dict-gaming-terms": { - "version": "1.0.4", - "dev": true - }, - "@cspell/dict-git": { - "version": "2.0.0", - "dev": true - }, - "@cspell/dict-golang": { - "version": "6.0.1", - "dev": true - }, - "@cspell/dict-haskell": { - "version": "4.0.1", - "dev": true - }, - "@cspell/dict-html": { - "version": "4.0.3", - "dev": true - }, - "@cspell/dict-html-symbol-entities": { - "version": "4.0.0", - "dev": true - }, - "@cspell/dict-java": { - "version": "5.0.5", - "dev": true - }, - "@cspell/dict-k8s": { - "version": "1.0.1", - "dev": true - }, - "@cspell/dict-latex": { - "version": "4.0.0", - "dev": true - }, - "@cspell/dict-lorem-ipsum": { - "version": "3.0.0", - "dev": true - }, - "@cspell/dict-lua": { - "version": "4.0.1", - "dev": true - }, - "@cspell/dict-node": { - "version": "4.0.2", - "dev": true - }, - "@cspell/dict-npm": { - "version": "5.0.5", - "dev": true - }, - "@cspell/dict-php": { - "version": "4.0.1", - "dev": true - }, - "@cspell/dict-powershell": { - "version": "5.0.1", - "dev": true - }, - "@cspell/dict-public-licenses": { - "version": "2.0.2", - "dev": true - }, - "@cspell/dict-python": { - "version": "4.0.4", - "dev": true - }, - "@cspell/dict-r": { - "version": "2.0.1", - "dev": true - }, - "@cspell/dict-ruby": { - "version": "5.0.0", - "dev": true - }, - "@cspell/dict-rust": { - "version": "4.0.1", - "dev": true - }, - "@cspell/dict-scala": { - "version": "5.0.0", - "dev": true - }, - "@cspell/dict-software-terms": { - "version": "3.1.8", - "dev": true - }, - "@cspell/dict-sql": { - "version": "2.1.0", - "dev": true - }, - "@cspell/dict-svelte": { - "version": "1.0.2", - "dev": true - }, - "@cspell/dict-swift": { - "version": "2.0.1", - "dev": true - }, - "@cspell/dict-typescript": { - "version": "3.1.1", - "dev": true - }, - "@cspell/dict-vue": { - "version": "3.0.0", - "dev": true - }, - "@cspell/dynamic-import": { - "version": "6.31.1", - "dev": true, - "requires": { - "import-meta-resolve": "^2.2.2" - } - }, - "@cspell/strong-weak-map": { - "version": "6.31.1", - "dev": true - }, - "@cspotcode/source-map-support": { - "version": "0.8.1", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "dependencies": { - "@jridgewell/trace-mapping": { - "version": "0.3.9", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - } - } - }, - "@eslint-community/eslint-utils": { - "version": "4.4.0", - "dev": true, - "requires": { - "eslint-visitor-keys": "^3.3.0" - } - }, - "@eslint-community/regexpp": { - "version": "4.5.1", - "dev": true - }, - "@eslint/eslintrc": { - "version": "2.0.3", - "dev": true, - "requires": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.5.2", - "globals": "^13.19.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" - } - }, - "@eslint/js": { - "version": "8.41.0", - "dev": true - }, - "@hapi/boom": { - "version": "10.0.1", - "requires": { - "@hapi/hoek": "^11.0.2" - }, - "dependencies": { - "@hapi/hoek": { - "version": "11.0.2" - } - } - }, - "@hapi/bourne": { - "version": "3.0.0" - }, - "@hapi/hoek": { - "version": "10.0.1" - }, - "@hapi/topo": { - "version": "5.1.0", - "requires": { - "@hapi/hoek": "^9.0.0" - }, - "dependencies": { - "@hapi/hoek": { - "version": "9.3.0" - } - } - }, - "@hapi/wreck": { - "version": "18.0.1", - "requires": { - "@hapi/boom": "^10.0.1", - "@hapi/bourne": "^3.0.0", - "@hapi/hoek": "^11.0.2" - }, - "dependencies": { - "@hapi/hoek": { - "version": "11.0.2" - } - } - }, - "@humanwhocodes/config-array": { - "version": "0.11.8", - "dev": true, - "requires": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.5" - } - }, - "@humanwhocodes/module-importer": { - "version": "1.0.1", - "dev": true - }, - "@humanwhocodes/object-schema": { - "version": "1.2.1", - "dev": true - }, - "@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "dev": true, - "requires": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "dependencies": { - "argparse": { - "version": "1.0.10", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "find-up": { - "version": "4.1.0", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "js-yaml": { - "version": "3.14.1", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-limit": { - "version": "2.3.0", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - } - } - }, - "@istanbuljs/schema": { - "version": "0.1.3", - "dev": true - }, - "@jest/console": { - "version": "29.5.0", - "dev": true, - "requires": { - "@jest/types": "^29.5.0", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^29.5.0", - "jest-util": "^29.5.0", - "slash": "^3.0.0" - } - }, - "@jest/core": { - "version": "29.5.0", - "dev": true, - "requires": { - "@jest/console": "^29.5.0", - "@jest/reporters": "^29.5.0", - "@jest/test-result": "^29.5.0", - "@jest/transform": "^29.5.0", - "@jest/types": "^29.5.0", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.5.0", - "jest-config": "^29.5.0", - "jest-haste-map": "^29.5.0", - "jest-message-util": "^29.5.0", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.5.0", - "jest-resolve-dependencies": "^29.5.0", - "jest-runner": "^29.5.0", - "jest-runtime": "^29.5.0", - "jest-snapshot": "^29.5.0", - "jest-util": "^29.5.0", - "jest-validate": "^29.5.0", - "jest-watcher": "^29.5.0", - "micromatch": "^4.0.4", - "pretty-format": "^29.5.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "@jest/environment": { - "version": "29.5.0", - "dev": true, - "requires": { - "@jest/fake-timers": "^29.5.0", - "@jest/types": "^29.5.0", - "@types/node": "*", - "jest-mock": "^29.5.0" - } - }, - "@jest/expect": { - "version": "29.5.0", - "dev": true, - "requires": { - "expect": "^29.5.0", - "jest-snapshot": "^29.5.0" - } - }, - "@jest/expect-utils": { - "version": "29.5.0", - "dev": true, - "requires": { - "jest-get-type": "^29.4.3" - } - }, - "@jest/fake-timers": { - "version": "29.5.0", - "dev": true, - "requires": { - "@jest/types": "^29.5.0", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.5.0", - "jest-mock": "^29.5.0", - "jest-util": "^29.5.0" - } - }, - "@jest/globals": { - "version": "29.5.0", - "dev": true, - "requires": { - "@jest/environment": "^29.5.0", - "@jest/expect": "^29.5.0", - "@jest/types": "^29.5.0", - "jest-mock": "^29.5.0" - } - }, - "@jest/reporters": { - "version": "29.5.0", - "dev": true, - "requires": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.5.0", - "@jest/test-result": "^29.5.0", - "@jest/transform": "^29.5.0", - "@jest/types": "^29.5.0", - "@jridgewell/trace-mapping": "^0.3.15", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^5.1.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.5.0", - "jest-util": "^29.5.0", - "jest-worker": "^29.5.0", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "v8-to-istanbul": "^9.0.1" - } - }, - "@jest/schemas": { - "version": "29.4.3", - "dev": true, - "requires": { - "@sinclair/typebox": "^0.25.16" - } - }, - "@jest/source-map": { - "version": "29.4.3", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.15", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" - } - }, - "@jest/test-result": { - "version": "29.5.0", - "dev": true, - "requires": { - "@jest/console": "^29.5.0", - "@jest/types": "^29.5.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - } - }, - "@jest/test-sequencer": { - "version": "29.5.0", - "dev": true, - "requires": { - "@jest/test-result": "^29.5.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.5.0", - "slash": "^3.0.0" - } - }, - "@jest/transform": { - "version": "29.5.0", - "dev": true, - "requires": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.5.0", - "@jridgewell/trace-mapping": "^0.3.15", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.5.0", - "jest-regex-util": "^29.4.3", - "jest-util": "^29.5.0", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.2" - } - }, - "@jest/types": { - "version": "29.5.0", - "dev": true, - "requires": { - "@jest/schemas": "^29.4.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - }, - "@jridgewell/gen-mapping": { - "version": "0.3.3", - "dev": true, - "requires": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "@jridgewell/resolve-uri": { - "version": "3.1.0", - "dev": true - }, - "@jridgewell/set-array": { - "version": "1.1.2", - "dev": true - }, - "@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "dev": true - }, - "@jridgewell/trace-mapping": { - "version": "0.3.18", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" - }, - "dependencies": { - "@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "dev": true - } - } - }, - "@microsoft/applicationinsights-web-snippet": { - "version": "1.0.1" - }, - "@nodelib/fs.scandir": { - "version": "2.1.5", - "dev": true, - "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.5", - "dev": true - }, - "@nodelib/fs.walk": { - "version": "1.2.8", - "dev": true, - "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - } - }, - "@octokit/auth-app": { - "version": "4.0.13", - "requires": { - "@octokit/auth-oauth-app": "^5.0.0", - "@octokit/auth-oauth-user": "^2.0.0", - "@octokit/request": "^6.0.0", - "@octokit/request-error": "^3.0.0", - "@octokit/types": "^9.0.0", - "deprecation": "^2.3.1", - "lru-cache": "^9.0.0", - "universal-github-app-jwt": "^1.1.1", - "universal-user-agent": "^6.0.0" - }, - "dependencies": { - "lru-cache": { - "version": "9.1.1" - } - } - }, - "@octokit/auth-oauth-app": { - "version": "5.0.5", - "requires": { - "@octokit/auth-oauth-device": "^4.0.0", - "@octokit/auth-oauth-user": "^2.0.0", - "@octokit/request": "^6.0.0", - "@octokit/types": "^9.0.0", - "@types/btoa-lite": "^1.0.0", - "btoa-lite": "^1.0.0", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/auth-oauth-device": { - "version": "4.0.4", - "requires": { - "@octokit/oauth-methods": "^2.0.0", - "@octokit/request": "^6.0.0", - "@octokit/types": "^9.0.0", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/auth-oauth-user": { - "version": "2.1.1", - "requires": { - "@octokit/auth-oauth-device": "^4.0.0", - "@octokit/oauth-methods": "^2.0.0", - "@octokit/request": "^6.0.0", - "@octokit/types": "^9.0.0", - "btoa-lite": "^1.0.0", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/auth-token": { - "version": "3.0.3", - "requires": { - "@octokit/types": "^9.0.0" - } - }, - "@octokit/core": { - "version": "4.2.1", - "requires": { - "@octokit/auth-token": "^3.0.0", - "@octokit/graphql": "^5.0.0", - "@octokit/request": "^6.0.0", - "@octokit/request-error": "^3.0.0", - "@octokit/types": "^9.0.0", - "before-after-hook": "^2.2.0", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/endpoint": { - "version": "7.0.5", - "requires": { - "@octokit/types": "^9.0.0", - "is-plain-object": "^5.0.0", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/graphql": { - "version": "5.0.5", - "requires": { - "@octokit/request": "^6.0.0", - "@octokit/types": "^9.0.0", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/oauth-authorization-url": { - "version": "5.0.0" - }, - "@octokit/oauth-methods": { - "version": "2.0.5", - "requires": { - "@octokit/oauth-authorization-url": "^5.0.0", - "@octokit/request": "^6.2.3", - "@octokit/request-error": "^3.0.3", - "@octokit/types": "^9.0.0", - "btoa-lite": "^1.0.0" - } - }, - "@octokit/openapi-types": { - "version": "17.2.0" - }, - "@octokit/plugin-paginate-graphql": { - "version": "2.0.1", - "requires": {} - }, - "@octokit/plugin-paginate-rest": { - "version": "6.1.2", - "requires": { - "@octokit/tsconfig": "^1.0.2", - "@octokit/types": "^9.2.3" - } - }, - "@octokit/plugin-request-log": { - "version": "1.0.4", - "requires": {} - }, - "@octokit/plugin-rest-endpoint-methods": { - "version": "7.1.2", - "requires": { - "@octokit/types": "^9.2.3", - "deprecation": "^2.3.1" - } - }, - "@octokit/request": { - "version": "6.2.5", - "requires": { - "@octokit/endpoint": "^7.0.0", - "@octokit/request-error": "^3.0.0", - "@octokit/types": "^9.0.0", - "is-plain-object": "^5.0.0", - "node-fetch": "^2.6.7", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/request-error": { - "version": "3.0.3", - "requires": { - "@octokit/types": "^9.0.0", - "deprecation": "^2.0.0", - "once": "^1.4.0" - } - }, - "@octokit/rest": { - "version": "19.0.11", - "requires": { - "@octokit/core": "^4.2.1", - "@octokit/plugin-paginate-rest": "^6.1.2", - "@octokit/plugin-request-log": "^1.0.4", - "@octokit/plugin-rest-endpoint-methods": "^7.1.2" - } - }, - "@octokit/tsconfig": { - "version": "1.0.2" - }, - "@octokit/types": { - "version": "9.2.3", - "requires": { - "@octokit/openapi-types": "^17.2.0" - } - }, - "@opentelemetry/api": { - "version": "1.4.1" - }, - "@opentelemetry/core": { - "version": "1.13.0", - "requires": { - "@opentelemetry/semantic-conventions": "1.13.0" - } - }, - "@opentelemetry/instrumentation": { - "version": "0.35.1", - "requires": { - "require-in-the-middle": "^5.0.3", - "semver": "^7.3.2", - "shimmer": "^1.2.1" - } - }, - "@opentelemetry/resources": { - "version": "1.13.0", - "requires": { - "@opentelemetry/core": "1.13.0", - "@opentelemetry/semantic-conventions": "1.13.0" - } - }, - "@opentelemetry/sdk-trace-base": { - "version": "1.13.0", - "requires": { - "@opentelemetry/core": "1.13.0", - "@opentelemetry/resources": "1.13.0", - "@opentelemetry/semantic-conventions": "1.13.0" - } - }, - "@opentelemetry/semantic-conventions": { - "version": "1.13.0" - }, - "@redis/bloom": { - "version": "1.2.0", - "requires": {} - }, - "@redis/client": { - "version": "1.5.9", - "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.5.9.tgz", - "integrity": "sha512-SffgN+P1zdWJWSXBvJeynvEnmnZrYmtKSRW00xl8pOPFOMJjxRR9u0frSxJpPR6Y4V+k54blJjGW7FgxbTI7bQ==", - "requires": { - "cluster-key-slot": "1.1.2", - "generic-pool": "3.9.0", - "yallist": "4.0.0" - } - }, - "@redis/graph": { - "version": "1.1.0", - "requires": {} - }, - "@redis/json": { - "version": "1.0.4", - "requires": {} - }, - "@redis/search": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.1.3.tgz", - "integrity": "sha512-4Dg1JjvCevdiCBTZqjhKkGoC5/BcB7k9j99kdMnaXFXg8x4eyOIVg9487CMv7/BUVkFLZCaIh8ead9mU15DNng==", - "requires": {} - }, - "@redis/time-series": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.0.5.tgz", - "integrity": "sha512-IFjIgTusQym2B5IZJG3XKr5llka7ey84fw/NOYqESP5WUfQs9zz1ww/9+qoz4ka/S6KcGBodzlCeZ5UImKbscg==", - "requires": {} - }, - "@sideway/address": { - "version": "4.1.4", - "requires": { - "@hapi/hoek": "^9.0.0" - }, - "dependencies": { - "@hapi/hoek": { - "version": "9.3.0" - } - } - }, - "@sideway/formula": { - "version": "3.0.1" - }, - "@sideway/pinpoint": { - "version": "2.0.0" - }, - "@sinclair/typebox": { - "version": "0.25.24", - "dev": true - }, - "@sinonjs/commons": { - "version": "2.0.0", - "dev": true, - "requires": { - "type-detect": "4.0.8" - } - }, - "@sinonjs/fake-timers": { - "version": "10.0.2", - "dev": true, - "requires": { - "@sinonjs/commons": "^2.0.0" - } - }, - "@tootallnate/once": { - "version": "2.0.0" - }, - "@ts-morph/common": { - "version": "0.12.3", - "dev": true, - "requires": { - "fast-glob": "^3.2.7", - "minimatch": "^3.0.4", - "mkdirp": "^1.0.4", - "path-browserify": "^1.0.1" - } - }, - "@tsconfig/node10": { - "version": "1.0.9", - "dev": true - }, - "@tsconfig/node12": { - "version": "1.0.11", - "dev": true - }, - "@tsconfig/node14": { - "version": "1.0.3", - "dev": true - }, - "@tsconfig/node16": { - "version": "1.0.3", - "dev": true - }, - "@types/babel__core": { - "version": "7.20.0", - "dev": true, - "requires": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "@types/babel__generator": { - "version": "7.6.4", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@types/babel__template": { - "version": "7.4.1", - "dev": true, - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@types/babel__traverse": { - "version": "7.18.5", - "dev": true, - "requires": { - "@babel/types": "^7.3.0" - } - }, - "@types/body-parser": { - "version": "1.19.2", - "requires": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "@types/btoa-lite": { - "version": "1.0.0" - }, - "@types/connect": { - "version": "3.4.35", - "requires": { - "@types/node": "*" - } - }, - "@types/debug": { - "version": "4.1.7", - "dev": true, - "requires": { - "@types/ms": "*" - } - }, - "@types/express": { - "version": "4.17.17", - "requires": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "@types/express-serve-static-core": { - "version": "4.17.34", - "requires": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "@types/express-session": { - "version": "1.17.7", - "dev": true, - "requires": { - "@types/express": "*" - } - }, - "@types/graceful-fs": { - "version": "4.1.6", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/is-buffer": { - "version": "2.0.0", - "requires": { - "@types/node": "*" - } - }, - "@types/istanbul-lib-coverage": { - "version": "2.0.4", - "dev": true - }, - "@types/istanbul-lib-report": { - "version": "3.0.0", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "*" - } - }, - "@types/istanbul-reports": { - "version": "3.0.1", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "@types/jest": { - "version": "29.5.1", - "dev": true, - "requires": { - "expect": "^29.0.0", - "pretty-format": "^29.0.0" - } - }, - "@types/json-schema": { - "version": "7.0.11", - "dev": true - }, - "@types/jsonwebtoken": { - "version": "9.0.2", - "requires": { - "@types/node": "*" - } - }, - "@types/lodash": { - "version": "4.14.194", - "dev": true - }, - "@types/luxon": { - "version": "3.3.0", - "dev": true - }, - "@types/memory-cache": { - "version": "0.2.2", - "dev": true - }, - "@types/mime": { - "version": "1.3.2" - }, - "@types/morgan": { - "version": "1.9.4", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/ms": { - "version": "0.7.31", - "dev": true - }, - "@types/node": { - "version": "20.4.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.2.tgz", - "integrity": "sha512-Dd0BYtWgnWJKwO1jkmTrzofjK2QXXcai0dmtzvIBhcA+RsG5h8R3xlyta0kGOZRNfL9GuRtb1knmPEhQrePCEw==" - }, - "@types/node-fetch": { - "version": "2.6.1", - "requires": { - "@types/node": "*", - "form-data": "^3.0.0" - }, - "dependencies": { - "form-data": { - "version": "3.0.1", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - } - } - }, - "@types/node-jose": { - "version": "1.1.10", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/oauth": { - "version": "0.9.1", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/object-path": { - "version": "0.11.1", - "dev": true - }, - "@types/parse-json": { - "version": "4.0.0", - "dev": true - }, - "@types/passport": { - "version": "1.0.12", - "dev": true, - "requires": { - "@types/express": "*" - } - }, - "@types/passport-azure-ad": { - "version": "4.3.1", - "dev": true, - "requires": { - "@types/express": "*", - "@types/passport": "*" - } - }, - "@types/passport-github": { - "version": "1.1.7", - "dev": true, - "requires": { - "@types/express": "*", - "@types/passport": "*", - "@types/passport-oauth2": "*" - } - }, - "@types/passport-oauth2": { - "version": "1.4.11", - "dev": true, - "requires": { - "@types/express": "*", - "@types/oauth": "*", - "@types/passport": "*" - } - }, - "@types/pg": { - "version": "8.10.1", - "dev": true, - "requires": { - "@types/node": "*", - "pg-protocol": "*", - "pg-types": "^4.0.1" - }, - "dependencies": { - "pg-types": { - "version": "4.0.1", - "dev": true, - "requires": { - "pg-int8": "1.0.1", - "pg-numeric": "1.0.2", - "postgres-array": "~3.0.1", - "postgres-bytea": "~3.0.0", - "postgres-date": "~2.0.1", - "postgres-interval": "^3.0.0", - "postgres-range": "^1.1.1" - } - }, - "postgres-array": { - "version": "3.0.2", - "dev": true - }, - "postgres-bytea": { - "version": "3.0.0", - "dev": true, - "requires": { - "obuf": "~1.1.2" - } - }, - "postgres-date": { - "version": "2.0.1", - "dev": true - }, - "postgres-interval": { - "version": "3.0.0", - "dev": true - } - } - }, - "@types/prettier": { - "version": "2.7.2", - "dev": true - }, - "@types/pug": { - "version": "2.0.6", - "dev": true - }, - "@types/qs": { - "version": "6.9.7" - }, - "@types/range-parser": { - "version": "1.2.4" - }, - "@types/recursive-readdir": { - "version": "2.2.1", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/semver": { - "version": "7.5.0", - "dev": true - }, - "@types/send": { - "version": "0.17.1", - "requires": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "@types/serve-static": { - "version": "1.13.10", - "requires": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "@types/simple-oauth2": { - "version": "5.0.4", - "dev": true - }, - "@types/stack-utils": { - "version": "2.0.1", - "dev": true - }, - "@types/tunnel": { - "version": "0.0.3", - "requires": { - "@types/node": "*" - } - }, - "@types/validator": { - "version": "13.7.17", - "dev": true - }, - "@types/yargs": { - "version": "17.0.24", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "@types/yargs-parser": { - "version": "21.0.0", - "dev": true - }, - "@typescript-eslint/eslint-plugin": { - "version": "5.59.7", - "dev": true, - "requires": { - "@eslint-community/regexpp": "^4.4.0", - "@typescript-eslint/scope-manager": "5.59.7", - "@typescript-eslint/type-utils": "5.59.7", - "@typescript-eslint/utils": "5.59.7", - "debug": "^4.3.4", - "grapheme-splitter": "^1.0.4", - "ignore": "^5.2.0", - "natural-compare-lite": "^1.4.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/parser": { - "version": "5.59.7", - "dev": true, - "requires": { - "@typescript-eslint/scope-manager": "5.59.7", - "@typescript-eslint/types": "5.59.7", - "@typescript-eslint/typescript-estree": "5.59.7", - "debug": "^4.3.4" - } - }, - "@typescript-eslint/scope-manager": { - "version": "5.59.7", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.59.7", - "@typescript-eslint/visitor-keys": "5.59.7" - } - }, - "@typescript-eslint/type-utils": { - "version": "5.59.7", - "dev": true, - "requires": { - "@typescript-eslint/typescript-estree": "5.59.7", - "@typescript-eslint/utils": "5.59.7", - "debug": "^4.3.4", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/types": { - "version": "5.59.7", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "5.59.7", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.59.7", - "@typescript-eslint/visitor-keys": "5.59.7", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/utils": { - "version": "5.59.7", - "dev": true, - "requires": { - "@eslint-community/eslint-utils": "^4.2.0", - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.59.7", - "@typescript-eslint/types": "5.59.7", - "@typescript-eslint/typescript-estree": "5.59.7", - "eslint-scope": "^5.1.1", - "semver": "^7.3.7" - } - }, - "@typescript-eslint/visitor-keys": { - "version": "5.59.7", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.59.7", - "eslint-visitor-keys": "^3.3.0" - } - }, - "accepts": { - "version": "1.3.8", - "requires": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - } - }, - "acorn": { - "version": "8.8.0", - "dev": true - }, - "acorn-jsx": { - "version": "5.3.2", - "dev": true, - "requires": {} - }, - "acorn-walk": { - "version": "8.2.0", - "dev": true - }, - "agent-base": { - "version": "6.0.2", - "requires": { - "debug": "4" - } - }, - "aggregate-error": { - "version": "3.1.0", - "dev": true, - "requires": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - } - }, - "ajv": { - "version": "6.12.6", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ansi-escapes": { - "version": "4.3.2", - "dev": true, - "requires": { - "type-fest": "^0.21.3" - }, - "dependencies": { - "type-fest": { - "version": "0.21.3", - "dev": true - } - } - }, - "ansi-regex": { - "version": "5.0.1", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "anymatch": { - "version": "3.1.3", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "app-root-path": { - "version": "3.1.0" - }, - "applicationinsights": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/applicationinsights/-/applicationinsights-2.7.0.tgz", - "integrity": "sha512-/vV5X6M4TlRA5NxNZAdCE0gukzfK24mb3z18D5Kl/CyIfSVIkafsIji3mK+Zi5q+7dn6H1CkFazlcnLf40anHw==", - "requires": { - "@azure/core-auth": "^1.4.0", - "@azure/core-rest-pipeline": "1.10.1", - "@azure/core-util": "1.2.0", - "@azure/opentelemetry-instrumentation-azure-sdk": "^1.0.0-beta.3", - "@microsoft/applicationinsights-web-snippet": "^1.0.1", - "@opentelemetry/api": "^1.0.4", - "@opentelemetry/core": "^1.12.0", - "@opentelemetry/sdk-trace-base": "^1.12.0", - "@opentelemetry/semantic-conventions": "^1.12.0", - "cls-hooked": "^4.2.2", - "continuation-local-storage": "^3.2.1", - "diagnostic-channel": "1.1.0", - "diagnostic-channel-publishers": "1.0.6" - } - }, - "arg": { - "version": "4.1.3", - "dev": true - }, - "argparse": { - "version": "2.0.1" - }, - "array-flatten": { - "version": "1.1.1" - }, - "array-timsort": { - "version": "1.0.3", - "dev": true - }, - "array-union": { - "version": "2.1.0", - "dev": true - }, - "asap": { - "version": "2.0.6" - }, - "assert-never": { - "version": "1.2.1" - }, - "astral-regex": { - "version": "2.0.0", - "dev": true - }, - "async-hook-jl": { - "version": "1.7.6", - "requires": { - "stack-chain": "^1.3.7" - } - }, - "async-listener": { - "version": "0.6.10", - "requires": { - "semver": "^5.3.0", - "shimmer": "^1.1.0" - }, - "dependencies": { - "semver": { - "version": "5.7.1" - } - } - }, - "async-prompt": { - "version": "1.0.1", - "requires": { - "keypress": "~0.2.1" - } - }, - "asynckit": { - "version": "0.4.0" - }, - "available-typed-arrays": { - "version": "1.0.5" - }, - "axios": { - "version": "1.4.0", - "requires": { - "follow-redirects": "^1.15.0", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, - "babel-jest": { - "version": "29.5.0", - "dev": true, - "requires": { - "@jest/transform": "^29.5.0", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.5.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - } - }, - "babel-plugin-istanbul": { - "version": "6.1.1", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - } - }, - "babel-plugin-jest-hoist": { - "version": "29.5.0", - "dev": true, - "requires": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - } - }, - "babel-preset-current-node-syntax": { - "version": "1.0.1", - "dev": true, - "requires": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" - } - }, - "babel-preset-jest": { - "version": "29.5.0", - "dev": true, - "requires": { - "babel-plugin-jest-hoist": "^29.5.0", - "babel-preset-current-node-syntax": "^1.0.0" - } - }, - "babel-walk": { - "version": "3.0.0-canary-5", - "requires": { - "@babel/types": "^7.9.6" - } - }, - "balanced-match": { - "version": "1.0.2" - }, - "base64-js": { - "version": "1.5.1" - }, - "base64url": { - "version": "3.0.1" - }, - "basic-auth": { - "version": "2.0.1", - "requires": { - "safe-buffer": "5.1.2" - } - }, - "before-after-hook": { - "version": "2.2.2" - }, - "body-parser": { - "version": "1.20.2", - "requires": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0" - }, - "on-finished": { - "version": "2.4.1", - "requires": { - "ee-first": "1.1.1" - } - } - } - }, - "brace-expansion": { - "version": "1.1.11", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "browserslist": { - "version": "4.21.5", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001449", - "electron-to-chromium": "^1.4.284", - "node-releases": "^2.0.8", - "update-browserslist-db": "^1.0.10" - } - }, - "bs-logger": { - "version": "0.2.6", - "dev": true, - "requires": { - "fast-json-stable-stringify": "2.x" - } - }, - "bser": { - "version": "2.1.1", - "dev": true, - "requires": { - "node-int64": "^0.4.0" - } - }, - "btoa-lite": { - "version": "1.0.0" - }, - "buffer": { - "version": "6.0.3", - "requires": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "buffer-equal-constant-time": { - "version": "1.0.1" - }, - "buffer-from": { - "version": "1.1.2", - "dev": true - }, - "buffer-writer": { - "version": "2.0.0" - }, - "builtins": { - "version": "5.0.1", - "dev": true, - "requires": { - "semver": "^7.0.0" - } - }, - "bunyan": { - "version": "1.8.15", - "requires": { - "dtrace-provider": "~0.8", - "moment": "^2.19.3", - "mv": "~2", - "safe-json-stringify": "~1" - } - }, - "bytes": { - "version": "3.1.2" - }, - "cache-manager": { - "version": "3.6.3", - "requires": { - "async": "3.2.3", - "lodash.clonedeep": "^4.5.0", - "lru-cache": "6.0.0" - }, - "dependencies": { - "async": { - "version": "3.2.3" - } - } - }, - "call-bind": { - "version": "1.0.2", - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - } - }, - "callsites": { - "version": "3.1.0", - "dev": true - }, - "camelcase": { - "version": "5.3.1", - "dev": true - }, - "caniuse-lite": { - "version": "1.0.30001486", - "dev": true - }, - "chalk": { - "version": "4.1.2", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "char-regex": { - "version": "1.0.2", - "dev": true - }, - "character-parser": { - "version": "2.2.0", - "requires": { - "is-regex": "^1.0.3" - } - }, - "ci-info": { - "version": "3.8.0", - "dev": true - }, - "cjs-module-lexer": { - "version": "1.2.2", - "dev": true - }, - "clean-stack": { - "version": "2.2.0", - "dev": true - }, - "clear-module": { - "version": "4.1.2", - "dev": true, - "requires": { - "parent-module": "^2.0.0", - "resolve-from": "^5.0.0" - } - }, - "cli-cursor": { - "version": "3.1.0", - "dev": true, - "requires": { - "restore-cursor": "^3.1.0" - } - }, - "cli-truncate": { - "version": "3.1.0", - "dev": true, - "requires": { - "slice-ansi": "^5.0.0", - "string-width": "^5.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "6.0.1", - "dev": true - }, - "emoji-regex": { - "version": "9.2.2", - "dev": true - }, - "string-width": { - "version": "5.1.2", - "dev": true, - "requires": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - } - }, - "strip-ansi": { - "version": "7.0.1", - "dev": true, - "requires": { - "ansi-regex": "^6.0.1" - } - } - } - }, - "cliui": { - "version": "8.0.1", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - } - }, - "cls-hooked": { - "version": "4.2.2", - "requires": { - "async-hook-jl": "^1.7.6", - "emitter-listener": "^1.0.1", - "semver": "^5.4.1" - }, - "dependencies": { - "semver": { - "version": "5.7.1" - } - } - }, - "cluster-key-slot": { - "version": "1.1.2" - }, - "co": { - "version": "4.6.0", - "dev": true - }, - "code-block-writer": { - "version": "11.0.3", - "dev": true - }, - "collect-v8-coverage": { - "version": "1.0.1", - "dev": true - }, - "color-contrast-checker": { - "version": "2.1.0" - }, - "color-convert": { - "version": "2.0.1", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "dev": true - }, - "colorette": { - "version": "2.0.20", - "dev": true - }, - "combined-stream": { - "version": "1.0.8", - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "commander": { - "version": "10.0.1", - "dev": true - }, - "comment-json": { - "version": "4.2.3", - "dev": true, - "requires": { - "array-timsort": "^1.0.3", - "core-util-is": "^1.0.3", - "esprima": "^4.0.1", - "has-own-prop": "^2.0.0", - "repeat-string": "^1.6.1" - } - }, - "compressible": { - "version": "2.0.18", - "requires": { - "mime-db": ">= 1.43.0 < 2" - } - }, - "compression": { - "version": "1.7.4", - "requires": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", - "debug": "2.6.9", - "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", - "vary": "~1.1.2" - }, - "dependencies": { - "bytes": { - "version": "3.0.0" - }, - "debug": { - "version": "2.6.9", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0" - } - } - }, - "concat-map": { - "version": "0.0.1" - }, - "configstore": { - "version": "5.0.1", - "dev": true, - "requires": { - "dot-prop": "^5.2.0", - "graceful-fs": "^4.1.2", - "make-dir": "^3.0.0", - "unique-string": "^2.0.0", - "write-file-atomic": "^3.0.0", - "xdg-basedir": "^4.0.0" - }, - "dependencies": { - "write-file-atomic": { - "version": "3.0.3", - "dev": true, - "requires": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - } - } - }, - "connect-redis": { - "version": "7.1.0", - "requires": {} - }, - "constantinople": { - "version": "4.0.1", - "requires": { - "@babel/parser": "^7.6.0", - "@babel/types": "^7.6.1" - } - }, - "content-disposition": { - "version": "0.5.4", - "requires": { - "safe-buffer": "5.2.1" - }, - "dependencies": { - "safe-buffer": { - "version": "5.2.1" - } - } - }, - "content-type": { - "version": "1.0.5" - }, - "continuation-local-storage": { - "version": "3.2.1", - "requires": { - "async-listener": "^0.6.0", - "emitter-listener": "^1.1.1" - } - }, - "convert-source-map": { - "version": "2.0.0", - "dev": true - }, - "cookie": { - "version": "0.5.0" - }, - "cookie-signature": { - "version": "1.0.6" - }, - "core-util-is": { - "version": "1.0.3", - "dev": true - }, - "cors": { - "version": "2.8.5", - "requires": { - "object-assign": "^4", - "vary": "^1" - } - }, - "cosmiconfig": { - "version": "8.0.0", - "dev": true, - "requires": { - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "parse-json": "^5.0.0", - "path-type": "^4.0.0" - } - }, - "create-require": { - "version": "1.1.1", - "dev": true - }, - "crypto-random-string": { - "version": "2.0.0", - "dev": true - }, - "cspell": { - "version": "6.31.1", - "dev": true, - "requires": { - "@cspell/cspell-pipe": "6.31.1", - "@cspell/dynamic-import": "6.31.1", - "chalk": "^4.1.2", - "commander": "^10.0.0", - "cspell-gitignore": "6.31.1", - "cspell-glob": "6.31.1", - "cspell-io": "6.31.1", - "cspell-lib": "6.31.1", - "fast-glob": "^3.2.12", - "fast-json-stable-stringify": "^2.1.0", - "file-entry-cache": "^6.0.1", - "get-stdin": "^8.0.0", - "imurmurhash": "^0.1.4", - "semver": "^7.3.8", - "strip-ansi": "^6.0.1", - "vscode-uri": "^3.0.7" - } - }, - "cspell-dictionary": { - "version": "6.31.1", - "dev": true, - "requires": { - "@cspell/cspell-pipe": "6.31.1", - "@cspell/cspell-types": "6.31.1", - "cspell-trie-lib": "6.31.1", - "fast-equals": "^4.0.3", - "gensequence": "^5.0.2" - } - }, - "cspell-gitignore": { - "version": "6.31.1", - "dev": true, - "requires": { - "cspell-glob": "6.31.1", - "find-up": "^5.0.0" - } - }, - "cspell-glob": { - "version": "6.31.1", - "dev": true, - "requires": { - "micromatch": "^4.0.5" - } - }, - "cspell-grammar": { - "version": "6.31.1", - "dev": true, - "requires": { - "@cspell/cspell-pipe": "6.31.1", - "@cspell/cspell-types": "6.31.1" - } - }, - "cspell-io": { - "version": "6.31.1", - "dev": true, - "requires": { - "@cspell/cspell-service-bus": "6.31.1", - "node-fetch": "^2.6.9" - } - }, - "cspell-lib": { - "version": "6.31.1", - "dev": true, - "requires": { - "@cspell/cspell-bundled-dicts": "6.31.1", - "@cspell/cspell-pipe": "6.31.1", - "@cspell/cspell-types": "6.31.1", - "@cspell/strong-weak-map": "6.31.1", - "clear-module": "^4.1.2", - "comment-json": "^4.2.3", - "configstore": "^5.0.1", - "cosmiconfig": "8.0.0", - "cspell-dictionary": "6.31.1", - "cspell-glob": "6.31.1", - "cspell-grammar": "6.31.1", - "cspell-io": "6.31.1", - "cspell-trie-lib": "6.31.1", - "fast-equals": "^4.0.3", - "find-up": "^5.0.0", - "gensequence": "^5.0.2", - "import-fresh": "^3.3.0", - "resolve-from": "^5.0.0", - "resolve-global": "^1.0.0", - "vscode-languageserver-textdocument": "^1.0.8", - "vscode-uri": "^3.0.7" - } - }, - "cspell-trie-lib": { - "version": "6.31.1", - "dev": true, - "requires": { - "@cspell/cspell-pipe": "6.31.1", - "@cspell/cspell-types": "6.31.1", - "gensequence": "^5.0.2" - } - }, - "debug": { - "version": "4.3.4", - "requires": { - "ms": "2.1.2" - } - }, - "dedent": { - "version": "0.7.0", - "dev": true - }, - "deeks": { - "version": "2.6.0" - }, - "deep-is": { - "version": "0.1.4", - "dev": true - }, - "deepmerge": { - "version": "4.3.1" - }, - "define-lazy-prop": { - "version": "2.0.0" - }, - "delayed-stream": { - "version": "1.0.0" - }, - "depd": { - "version": "2.0.0" - }, - "deprecation": { - "version": "2.3.1" - }, - "destroy": { - "version": "1.2.0" - }, - "detect-newline": { - "version": "3.1.0", - "dev": true - }, - "diagnostic-channel": { - "version": "1.1.0", - "requires": { - "semver": "^5.3.0" - }, - "dependencies": { - "semver": { - "version": "5.7.1" - } - } - }, - "diagnostic-channel-publishers": { - "version": "1.0.6", - "requires": {} - }, - "diff": { - "version": "4.0.2", - "dev": true - }, - "diff-sequences": { - "version": "29.4.3", - "dev": true - }, - "dir-glob": { - "version": "3.0.1", - "dev": true, - "requires": { - "path-type": "^4.0.0" - } - }, - "doc-path": { - "version": "3.0.6" - }, - "doctrine": { - "version": "3.0.0", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "doctypes": { - "version": "1.1.0" - }, - "dot-prop": { - "version": "5.3.0", - "dev": true, - "requires": { - "is-obj": "^2.0.0" - } - }, - "dotenv": { - "version": "16.3.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", - "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==" - }, - "dtrace-provider": { - "version": "0.8.8", - "optional": true, - "requires": { - "nan": "^2.14.0" - } - }, - "eastasianwidth": { - "version": "0.2.0", - "dev": true - }, - "ecdsa-sig-formatter": { - "version": "1.0.11", - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "ee-first": { - "version": "1.1.1" - }, - "electron-to-chromium": { - "version": "1.4.392", - "dev": true - }, - "emitter-listener": { - "version": "1.1.2", - "requires": { - "shimmer": "^1.2.0" - } - }, - "emittery": { - "version": "0.13.1", - "dev": true - }, - "emoji-regex": { - "version": "8.0.0", - "dev": true - }, - "encodeurl": { - "version": "1.0.2" - }, - "error-ex": { - "version": "1.3.2", - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "es6-promise": { - "version": "4.2.8" - }, - "escalade": { - "version": "3.1.1", - "dev": true - }, - "escape-html": { - "version": "1.0.3" - }, - "escape-string-regexp": { - "version": "4.0.0", - "dev": true - }, - "eslint": { - "version": "8.41.0", - "dev": true, - "requires": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.0.3", - "@eslint/js": "8.41.0", - "@humanwhocodes/config-array": "^0.11.8", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.0", - "eslint-visitor-keys": "^3.4.1", - "espree": "^9.5.2", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0" - }, - "dependencies": { - "cross-spawn": { - "version": "7.0.3", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "eslint-scope": { - "version": "7.2.0", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - } - }, - "estraverse": { - "version": "5.3.0", - "dev": true - } - } - }, - "eslint-config-prettier": { - "version": "8.8.0", - "dev": true, - "requires": {} - }, - "eslint-plugin-es-x": { - "version": "6.2.1", - "dev": true, - "requires": { - "@eslint-community/eslint-utils": "^4.1.2", - "@eslint-community/regexpp": "^4.5.0" - } - }, - "eslint-plugin-n": { - "version": "16.0.0", - "dev": true, - "requires": { - "@eslint-community/eslint-utils": "^4.4.0", - "builtins": "^5.0.1", - "eslint-plugin-es-x": "^6.1.0", - "ignore": "^5.1.1", - "is-core-module": "^2.12.0", - "minimatch": "^3.1.2", - "resolve": "^1.22.2", - "semver": "^7.5.0" - } - }, - "eslint-plugin-prettier": { - "version": "4.2.1", - "dev": true, - "requires": { - "prettier-linter-helpers": "^1.0.0" - } - }, - "eslint-scope": { - "version": "5.1.1", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - } - }, - "eslint-visitor-keys": { - "version": "3.4.1", - "dev": true - }, - "espree": { - "version": "9.5.2", - "dev": true, - "requires": { - "acorn": "^8.8.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - } - }, - "esprima": { - "version": "4.0.1", - "dev": true - }, - "esquery": { - "version": "1.5.0", - "dev": true, - "requires": { - "estraverse": "^5.1.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "dev": true - } - } - }, - "esrecurse": { - "version": "4.3.0", - "dev": true, - "requires": { - "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "dev": true - } - } - }, - "estraverse": { - "version": "4.3.0", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "dev": true - }, - "etag": { - "version": "1.8.1" - }, - "events": { - "version": "3.3.0" - }, - "execa": { - "version": "5.1.1", - "dev": true, - "requires": { - "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" - }, - "dependencies": { - "cross-spawn": { - "version": "7.0.3", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - } - } - }, - "exit": { - "version": "0.1.2", - "dev": true - }, - "expect": { - "version": "29.5.0", - "dev": true, - "requires": { - "@jest/expect-utils": "^29.5.0", - "jest-get-type": "^29.4.3", - "jest-matcher-utils": "^29.5.0", - "jest-message-util": "^29.5.0", - "jest-util": "^29.5.0" - } - }, - "express": { - "version": "4.18.2", - "requires": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.1", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.5.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.2.0", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.11.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "dependencies": { - "body-parser": { - "version": "1.20.1", - "requires": { - "bytes": "3.1.2", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.1", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - } - }, - "debug": { - "version": "2.6.9", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0" - }, - "on-finished": { - "version": "2.4.1", - "requires": { - "ee-first": "1.1.1" - } - }, - "raw-body": { - "version": "2.5.1", - "requires": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - } - }, - "safe-buffer": { - "version": "5.2.1" - } - } - }, - "express-async-handler": { - "version": "1.1.4" - }, - "express-session": { - "version": "1.17.3", - "requires": { - "cookie": "0.4.2", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~2.0.0", - "on-headers": "~1.0.2", - "parseurl": "~1.3.3", - "safe-buffer": "5.2.1", - "uid-safe": "~2.1.5" - }, - "dependencies": { - "cookie": { - "version": "0.4.2" - }, - "debug": { - "version": "2.6.9", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0" - }, - "safe-buffer": { - "version": "5.2.1" - } - } - }, - "express-sslify": { - "version": "1.2.0" - }, - "fast-deep-equal": { - "version": "3.1.3", - "dev": true - }, - "fast-diff": { - "version": "1.2.0", - "dev": true - }, - "fast-equals": { - "version": "4.0.3", - "dev": true - }, - "fast-glob": { - "version": "3.2.12", - "dev": true, - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "dependencies": { - "glob-parent": { - "version": "5.1.2", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - } - } - }, - "fast-json-stable-stringify": { - "version": "2.1.0" - }, - "fast-levenshtein": { - "version": "2.0.6", - "dev": true - }, - "fast-safe-stringify": { - "version": "2.1.1" - }, - "fast-xml-parser": { - "version": "4.2.2", - "requires": { - "strnum": "^1.0.5" - } - }, - "fastq": { - "version": "1.15.0", - "dev": true, - "requires": { - "reusify": "^1.0.4" - } - }, - "fb-watchman": { - "version": "2.0.2", - "dev": true, - "requires": { - "bser": "2.1.1" - } - }, - "file-entry-cache": { - "version": "6.0.1", - "dev": true, - "requires": { - "flat-cache": "^3.0.4" - } - }, - "file-size": { - "version": "1.0.0" - }, - "fill-range": { - "version": "7.0.1", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "finalhandler": { - "version": "1.2.0", - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0" - }, - "on-finished": { - "version": "2.4.1", - "requires": { - "ee-first": "1.1.1" - } - } - } - }, - "find-up": { - "version": "5.0.0", - "dev": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "flat-cache": { - "version": "3.0.4", - "dev": true, - "requires": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - } - }, - "flatted": { - "version": "3.2.5", - "dev": true - }, - "follow-redirects": { - "version": "1.15.2" - }, - "for-each": { - "version": "0.3.3", - "requires": { - "is-callable": "^1.1.3" - } - }, - "form-data": { - "version": "4.0.0", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - }, - "forwarded": { - "version": "0.2.0" - }, - "fresh": { - "version": "0.5.2" - }, - "fs.realpath": { - "version": "1.0.0" - }, - "fsevents": { - "version": "2.3.2", - "dev": true, - "optional": true - }, - "function-bind": { - "version": "1.1.1" - }, - "generic-pool": { - "version": "3.9.0" - }, - "gensequence": { - "version": "5.0.2", - "dev": true - }, - "gensync": { - "version": "1.0.0-beta.2", - "dev": true - }, - "get-caller-file": { - "version": "2.0.5", - "dev": true - }, - "get-intrinsic": { - "version": "1.2.0", - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" - } - }, - "get-package-type": { - "version": "0.1.0", - "dev": true - }, - "get-stdin": { - "version": "8.0.0", - "dev": true - }, - "get-stream": { - "version": "6.0.1", - "dev": true - }, - "github-username-regex": { - "version": "1.0.0" - }, - "glob": { - "version": "7.2.0", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "6.0.2", - "dev": true, - "requires": { - "is-glob": "^4.0.3" - } - }, - "global-dirs": { - "version": "0.1.1", - "dev": true, - "requires": { - "ini": "^1.3.4" - } - }, - "globals": { - "version": "13.20.0", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "globby": { - "version": "11.1.0", - "dev": true, - "requires": { - "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" - } - }, - "gopd": { - "version": "1.0.1", - "requires": { - "get-intrinsic": "^1.1.3" - } - }, - "graceful-fs": { - "version": "4.2.10" - }, - "grapheme-splitter": { - "version": "1.0.4", - "dev": true - }, - "graphemer": { - "version": "1.4.0", - "dev": true - }, - "has": { - "version": "1.0.3", - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-flag": { - "version": "4.0.0", - "dev": true - }, - "has-own-prop": { - "version": "2.0.0", - "dev": true - }, - "has-symbols": { - "version": "1.0.3" - }, - "has-tostringtag": { - "version": "1.0.0", - "requires": { - "has-symbols": "^1.0.2" - } - }, - "hsts": { - "version": "2.2.0", - "requires": { - "depd": "2.0.0" - } - }, - "html-escaper": { - "version": "2.0.2", - "dev": true - }, - "http-errors": { - "version": "2.0.0", - "requires": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - } - }, - "http-proxy-agent": { - "version": "5.0.0", - "requires": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" - } - }, - "https-proxy-agent": { - "version": "5.0.0", - "requires": { - "agent-base": "6", - "debug": "4" - } - }, - "human-signals": { - "version": "2.1.0", - "dev": true - }, - "husky": { - "version": "8.0.3", - "dev": true - }, - "iconv-lite": { - "version": "0.4.24", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ieee754": { - "version": "1.2.1" - }, - "ignore": { - "version": "5.2.0", - "dev": true - }, - "import-fresh": { - "version": "3.3.0", - "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "dependencies": { - "parent-module": { - "version": "1.0.1", - "dev": true, - "requires": { - "callsites": "^3.0.0" - } - }, - "resolve-from": { - "version": "4.0.0", - "dev": true - } - } - }, - "import-local": { - "version": "3.1.0", - "dev": true, - "requires": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - } - }, - "import-meta-resolve": { - "version": "2.2.2", - "dev": true - }, - "imurmurhash": { - "version": "0.1.4", - "dev": true - }, - "indent-string": { - "version": "4.0.0", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4" - }, - "ini": { - "version": "1.3.8", - "dev": true - }, - "ipaddr.js": { - "version": "1.9.1" - }, - "is-arguments": { - "version": "1.1.1", - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-arrayish": { - "version": "0.2.1" - }, - "is-buffer": { - "version": "2.0.5" - }, - "is-callable": { - "version": "1.2.7" - }, - "is-core-module": { - "version": "2.12.0", - "requires": { - "has": "^1.0.3" - } - }, - "is-docker": { - "version": "2.2.1" - }, - "is-expression": { - "version": "4.0.0", - "requires": { - "acorn": "^7.1.1", - "object-assign": "^4.1.1" - }, - "dependencies": { - "acorn": { - "version": "7.4.1" - } - } - }, - "is-extglob": { - "version": "2.1.1", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "dev": true - }, - "is-generator-fn": { - "version": "2.1.0", - "dev": true - }, - "is-generator-function": { - "version": "1.0.10", - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-glob": { - "version": "4.0.3", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "7.0.0", - "dev": true - }, - "is-obj": { - "version": "2.0.0", - "dev": true - }, - "is-path-inside": { - "version": "3.0.3", - "dev": true - }, - "is-plain-object": { - "version": "5.0.0" - }, - "is-promise": { - "version": "2.2.2" - }, - "is-regex": { - "version": "1.1.4", - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-stream": { - "version": "2.0.1", - "dev": true - }, - "is-typed-array": { - "version": "1.1.10", - "requires": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" - } - }, - "is-typedarray": { - "version": "1.0.0", - "dev": true - }, - "is-wsl": { - "version": "2.2.0", - "requires": { - "is-docker": "^2.0.0" - } - }, - "isexe": { - "version": "2.0.0", - "dev": true - }, - "istanbul-lib-coverage": { - "version": "3.2.0", - "dev": true - }, - "istanbul-lib-instrument": { - "version": "5.2.1", - "dev": true, - "requires": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "dev": true - } - } - }, - "istanbul-lib-report": { - "version": "3.0.0", - "dev": true, - "requires": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" - } - }, - "istanbul-lib-source-maps": { - "version": "4.0.1", - "dev": true, - "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - } - }, - "istanbul-reports": { - "version": "3.1.5", - "dev": true, - "requires": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - } - }, - "jest": { - "version": "29.5.0", - "dev": true, - "requires": { - "@jest/core": "^29.5.0", - "@jest/types": "^29.5.0", - "import-local": "^3.0.2", - "jest-cli": "^29.5.0" - } - }, - "jest-changed-files": { - "version": "29.5.0", - "dev": true, - "requires": { - "execa": "^5.0.0", - "p-limit": "^3.1.0" - } - }, - "jest-circus": { - "version": "29.5.0", - "dev": true, - "requires": { - "@jest/environment": "^29.5.0", - "@jest/expect": "^29.5.0", - "@jest/test-result": "^29.5.0", - "@jest/types": "^29.5.0", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^0.7.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.5.0", - "jest-matcher-utils": "^29.5.0", - "jest-message-util": "^29.5.0", - "jest-runtime": "^29.5.0", - "jest-snapshot": "^29.5.0", - "jest-util": "^29.5.0", - "p-limit": "^3.1.0", - "pretty-format": "^29.5.0", - "pure-rand": "^6.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - } - }, - "jest-cli": { - "version": "29.5.0", - "dev": true, - "requires": { - "@jest/core": "^29.5.0", - "@jest/test-result": "^29.5.0", - "@jest/types": "^29.5.0", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "import-local": "^3.0.2", - "jest-config": "^29.5.0", - "jest-util": "^29.5.0", - "jest-validate": "^29.5.0", - "prompts": "^2.0.1", - "yargs": "^17.3.1" - } - }, - "jest-config": { - "version": "29.5.0", - "dev": true, - "requires": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.5.0", - "@jest/types": "^29.5.0", - "babel-jest": "^29.5.0", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.5.0", - "jest-environment-node": "^29.5.0", - "jest-get-type": "^29.4.3", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.5.0", - "jest-runner": "^29.5.0", - "jest-util": "^29.5.0", - "jest-validate": "^29.5.0", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.5.0", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - } - }, - "jest-diff": { - "version": "29.5.0", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^29.4.3", - "jest-get-type": "^29.4.3", - "pretty-format": "^29.5.0" - } - }, - "jest-docblock": { - "version": "29.4.3", - "dev": true, - "requires": { - "detect-newline": "^3.0.0" - } - }, - "jest-each": { - "version": "29.5.0", - "dev": true, - "requires": { - "@jest/types": "^29.5.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.4.3", - "jest-util": "^29.5.0", - "pretty-format": "^29.5.0" - } - }, - "jest-environment-node": { - "version": "29.5.0", - "dev": true, - "requires": { - "@jest/environment": "^29.5.0", - "@jest/fake-timers": "^29.5.0", - "@jest/types": "^29.5.0", - "@types/node": "*", - "jest-mock": "^29.5.0", - "jest-util": "^29.5.0" - } - }, - "jest-get-type": { - "version": "29.4.3", - "dev": true - }, - "jest-haste-map": { - "version": "29.5.0", - "dev": true, - "requires": { - "@jest/types": "^29.5.0", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "fsevents": "^2.3.2", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.4.3", - "jest-util": "^29.5.0", - "jest-worker": "^29.5.0", - "micromatch": "^4.0.4", - "walker": "^1.0.8" - } - }, - "jest-junit": { - "version": "16.0.0", - "dev": true, - "requires": { - "mkdirp": "^1.0.4", - "strip-ansi": "^6.0.1", - "uuid": "^8.3.2", - "xml": "^1.0.1" - } - }, - "jest-leak-detector": { - "version": "29.5.0", - "dev": true, - "requires": { - "jest-get-type": "^29.4.3", - "pretty-format": "^29.5.0" - } - }, - "jest-matcher-utils": { - "version": "29.5.0", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "jest-diff": "^29.5.0", - "jest-get-type": "^29.4.3", - "pretty-format": "^29.5.0" - } - }, - "jest-message-util": { - "version": "29.5.0", - "dev": true, - "requires": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.5.0", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.5.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - } - }, - "jest-mock": { - "version": "29.5.0", - "dev": true, - "requires": { - "@jest/types": "^29.5.0", - "@types/node": "*", - "jest-util": "^29.5.0" - } - }, - "jest-pnp-resolver": { - "version": "1.2.3", - "dev": true, - "requires": {} - }, - "jest-regex-util": { - "version": "29.4.3", - "dev": true - }, - "jest-resolve": { - "version": "29.5.0", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.5.0", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.5.0", - "jest-validate": "^29.5.0", - "resolve": "^1.20.0", - "resolve.exports": "^2.0.0", - "slash": "^3.0.0" - } - }, - "jest-resolve-dependencies": { - "version": "29.5.0", - "dev": true, - "requires": { - "jest-regex-util": "^29.4.3", - "jest-snapshot": "^29.5.0" - } - }, - "jest-runner": { - "version": "29.5.0", - "dev": true, - "requires": { - "@jest/console": "^29.5.0", - "@jest/environment": "^29.5.0", - "@jest/test-result": "^29.5.0", - "@jest/transform": "^29.5.0", - "@jest/types": "^29.5.0", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.4.3", - "jest-environment-node": "^29.5.0", - "jest-haste-map": "^29.5.0", - "jest-leak-detector": "^29.5.0", - "jest-message-util": "^29.5.0", - "jest-resolve": "^29.5.0", - "jest-runtime": "^29.5.0", - "jest-util": "^29.5.0", - "jest-watcher": "^29.5.0", - "jest-worker": "^29.5.0", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - } - }, - "jest-runtime": { - "version": "29.5.0", - "dev": true, - "requires": { - "@jest/environment": "^29.5.0", - "@jest/fake-timers": "^29.5.0", - "@jest/globals": "^29.5.0", - "@jest/source-map": "^29.4.3", - "@jest/test-result": "^29.5.0", - "@jest/transform": "^29.5.0", - "@jest/types": "^29.5.0", - "@types/node": "*", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.5.0", - "jest-message-util": "^29.5.0", - "jest-mock": "^29.5.0", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.5.0", - "jest-snapshot": "^29.5.0", - "jest-util": "^29.5.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - } - }, - "jest-snapshot": { - "version": "29.5.0", - "dev": true, - "requires": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/traverse": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.5.0", - "@jest/transform": "^29.5.0", - "@jest/types": "^29.5.0", - "@types/babel__traverse": "^7.0.6", - "@types/prettier": "^2.1.5", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.5.0", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.5.0", - "jest-get-type": "^29.4.3", - "jest-matcher-utils": "^29.5.0", - "jest-message-util": "^29.5.0", - "jest-util": "^29.5.0", - "natural-compare": "^1.4.0", - "pretty-format": "^29.5.0", - "semver": "^7.3.5" - } - }, - "jest-util": { - "version": "29.5.0", - "dev": true, - "requires": { - "@jest/types": "^29.5.0", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - } - }, - "jest-validate": { - "version": "29.5.0", - "dev": true, - "requires": { - "@jest/types": "^29.5.0", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.4.3", - "leven": "^3.1.0", - "pretty-format": "^29.5.0" - }, - "dependencies": { - "camelcase": { - "version": "6.3.0", - "dev": true - } - } - }, - "jest-watcher": { - "version": "29.5.0", - "dev": true, - "requires": { - "@jest/test-result": "^29.5.0", - "@jest/types": "^29.5.0", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.5.0", - "string-length": "^4.0.1" - } - }, - "jest-worker": { - "version": "29.5.0", - "dev": true, - "requires": { - "@types/node": "*", - "jest-util": "^29.5.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "dependencies": { - "supports-color": { - "version": "8.1.1", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "joi": { - "version": "17.9.2", - "requires": { - "@hapi/hoek": "^9.0.0", - "@hapi/topo": "^5.0.0", - "@sideway/address": "^4.1.3", - "@sideway/formula": "^3.0.1", - "@sideway/pinpoint": "^2.0.0" - }, - "dependencies": { - "@hapi/hoek": { - "version": "9.3.0" - } - } - }, - "jose": { - "version": "4.14.4" - }, - "js-stringify": { - "version": "1.0.2" - }, - "js-tokens": { - "version": "4.0.0", - "dev": true - }, - "js-yaml": { - "version": "4.1.0", - "requires": { - "argparse": "^2.0.1" - } - }, - "jsbi": { - "version": "3.2.5" - }, - "jsesc": { - "version": "2.5.2", - "dev": true - }, - "json-2-csv": { - "version": "3.18.0", - "requires": { - "deeks": "2.6.0", - "doc-path": "3.0.6" - } - }, - "json-parse-better-errors": { - "version": "1.0.2" - }, - "json-parse-even-better-errors": { - "version": "2.3.1", - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "dev": true - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "dev": true - }, - "json5": { - "version": "2.2.3", - "dev": true - }, - "jsonc": { - "version": "2.0.0", - "requires": { - "fast-safe-stringify": "^2.0.6", - "graceful-fs": "^4.1.15", - "mkdirp": "^0.5.1", - "parse-json": "^4.0.0", - "strip-bom": "^4.0.0", - "strip-json-comments": "^3.0.1" - }, - "dependencies": { - "mkdirp": { - "version": "0.5.6", - "requires": { - "minimist": "^1.2.6" - } - }, - "parse-json": { - "version": "4.0.0", - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - } - } - }, - "jsonwebtoken": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.1.tgz", - "integrity": "sha512-K8wx7eJ5TPvEjuiVSkv167EVboBDv9PZdDoF7BgeQnBLVvZWW9clr2PsQHVJDTKaEIH5JBIwHujGcHp7GgI2eg==", - "requires": { - "jws": "^3.2.2", - "lodash": "^4.17.21", - "ms": "^2.1.1", - "semver": "^7.3.8" - }, - "dependencies": { - "jwa": { - "version": "1.4.1", - "requires": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "jws": { - "version": "3.2.2", - "requires": { - "jwa": "^1.4.1", - "safe-buffer": "^5.0.1" - } - } - } - }, - "jssha": { - "version": "3.3.0" - }, - "jstransformer": { - "version": "1.0.0", - "requires": { - "is-promise": "^2.0.0", - "promise": "^7.0.1" - } - }, - "jwa": { - "version": "2.0.0", - "requires": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "jwks-rsa": { - "version": "3.0.1", - "requires": { - "@types/express": "^4.17.14", - "@types/jsonwebtoken": "^9.0.0", - "debug": "^4.3.4", - "jose": "^4.10.4", - "limiter": "^1.1.5", - "lru-memoizer": "^2.1.4" - } - }, - "jws": { - "version": "4.0.0", - "requires": { - "jwa": "^2.0.0", - "safe-buffer": "^5.0.1" - } - }, - "keypress": { - "version": "0.2.1" - }, - "kleur": { - "version": "3.0.3", - "dev": true - }, - "language-map": { - "version": "1.5.0" - }, - "leven": { - "version": "3.1.0", - "dev": true - }, - "levn": { - "version": "0.4.1", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - } - }, - "lilconfig": { - "version": "2.1.0", - "dev": true - }, - "limiter": { - "version": "1.1.5" - }, - "lines-and-columns": { - "version": "1.2.4", - "dev": true - }, - "linkify-it": { - "version": "4.0.1", - "dev": true, - "requires": { - "uc.micro": "^1.0.1" - } - }, - "lint-staged": { - "version": "13.2.2", - "dev": true, - "requires": { - "chalk": "5.2.0", - "cli-truncate": "^3.1.0", - "commander": "^10.0.0", - "debug": "^4.3.4", - "execa": "^7.0.0", - "lilconfig": "2.1.0", - "listr2": "^5.0.7", - "micromatch": "^4.0.5", - "normalize-path": "^3.0.0", - "object-inspect": "^1.12.3", - "pidtree": "^0.6.0", - "string-argv": "^0.3.1", - "yaml": "^2.2.2" - }, - "dependencies": { - "chalk": { - "version": "5.2.0", - "dev": true - }, - "cross-spawn": { - "version": "7.0.3", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "execa": { - "version": "7.1.1", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.1", - "human-signals": "^4.3.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^3.0.7", - "strip-final-newline": "^3.0.0" - } - }, - "human-signals": { - "version": "4.3.1", - "dev": true - }, - "is-stream": { - "version": "3.0.0", - "dev": true - }, - "mimic-fn": { - "version": "4.0.0", - "dev": true - }, - "npm-run-path": { - "version": "5.1.0", - "dev": true, - "requires": { - "path-key": "^4.0.0" - }, - "dependencies": { - "path-key": { - "version": "4.0.0", - "dev": true - } - } - }, - "onetime": { - "version": "6.0.0", - "dev": true, - "requires": { - "mimic-fn": "^4.0.0" - } - }, - "strip-final-newline": { - "version": "3.0.0", - "dev": true - } - } - }, - "listr2": { - "version": "5.0.8", - "dev": true, - "requires": { - "cli-truncate": "^2.1.0", - "colorette": "^2.0.19", - "log-update": "^4.0.0", - "p-map": "^4.0.0", - "rfdc": "^1.3.0", - "rxjs": "^7.8.0", - "through": "^2.3.8", - "wrap-ansi": "^7.0.0" - }, - "dependencies": { - "cli-truncate": { - "version": "2.1.0", - "dev": true, - "requires": { - "slice-ansi": "^3.0.0", - "string-width": "^4.2.0" - } - }, - "slice-ansi": { - "version": "3.0.0", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - } - } - } - }, - "locate-path": { - "version": "6.0.0", - "dev": true, - "requires": { - "p-locate": "^5.0.0" - } - }, - "lodash": { - "version": "4.17.21" - }, - "lodash.clonedeep": { - "version": "4.5.0" - }, - "lodash.memoize": { - "version": "4.1.2", - "dev": true - }, - "lodash.merge": { - "version": "4.6.2", - "dev": true - }, - "log-update": { - "version": "4.0.0", - "dev": true, - "requires": { - "ansi-escapes": "^4.3.0", - "cli-cursor": "^3.1.0", - "slice-ansi": "^4.0.0", - "wrap-ansi": "^6.2.0" - }, - "dependencies": { - "slice-ansi": { - "version": "4.0.0", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - } - }, - "wrap-ansi": { - "version": "6.2.0", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - } - } - }, - "long": { - "version": "5.2.0" - }, - "lru-cache": { - "version": "6.0.0", - "requires": { - "yallist": "^4.0.0" - } - }, - "lru-memoizer": { - "version": "2.1.4", - "requires": { - "lodash.clonedeep": "^4.5.0", - "lru-cache": "~4.0.0" - }, - "dependencies": { - "lru-cache": { - "version": "4.0.2", - "requires": { - "pseudomap": "^1.0.1", - "yallist": "^2.0.0" - } - }, - "yallist": { - "version": "2.1.2" - } - } - }, - "luxon": { - "version": "3.3.0" - }, - "make-dir": { - "version": "3.1.0", - "dev": true, - "requires": { - "semver": "^6.0.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "dev": true - } - } - }, - "make-error": { - "version": "1.3.6", - "dev": true - }, - "makeerror": { - "version": "1.0.12", - "dev": true, - "requires": { - "tmpl": "1.0.5" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "markdown-it": { - "version": "13.0.1", + "node_modules/markdownlint-cli2/node_modules/path-type": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-5.0.0.tgz", + "integrity": "sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==", "dev": true, - "requires": { - "argparse": "^2.0.1", - "entities": "~3.0.1", - "linkify-it": "^4.0.1", - "mdurl": "^1.0.1", - "uc.micro": "^1.0.5" + "engines": { + "node": ">=12" }, - "dependencies": { - "entities": { - "version": "3.0.1", - "dev": true - } + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "markdownlint": { - "version": "0.28.2", + "node_modules/markdownlint-cli2/node_modules/slash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", "dev": true, - "requires": { - "markdown-it": "13.0.1", - "markdownlint-micromark": "0.1.2" + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "markdownlint-cli2": { - "version": "0.7.1", + "node_modules/markdownlint-cli2/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, - "requires": { - "globby": "13.1.4", - "markdownlint": "0.28.2", - "markdownlint-cli2-formatter-default": "0.0.4", - "micromatch": "4.0.5", - "strip-json-comments": "5.0.0", - "yaml": "2.2.2" - }, - "dependencies": { - "globby": { - "version": "13.1.4", - "dev": true, - "requires": { - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.11", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^4.0.0" - } - }, - "slash": { - "version": "4.0.0", - "dev": true - }, - "strip-json-comments": { - "version": "5.0.0", - "dev": true - } + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "markdownlint-cli2-formatter-default": { - "version": "0.0.4", + "node_modules/markdownlint-micromark": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/markdownlint-micromark/-/markdownlint-micromark-0.1.8.tgz", + "integrity": "sha512-1ouYkMRo9/6gou9gObuMDnvZM8jC/ly3QCFQyoSPCS2XV1ZClU0xpKbL1Ar3bWWRT1RnBZkWUEiNKrI2CwiBQA==", "dev": true, - "requires": {} - }, - "markdownlint-micromark": { - "version": "0.1.2", - "dev": true + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/DavidAnson" + } }, - "mdurl": { - "version": "1.0.1", + "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==", "dev": true }, - "media-typer": { - "version": "0.3.0" + "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" + } }, - "memory-cache": { - "version": "0.2.0" + "node_modules/memory-cache": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/memory-cache/-/memory-cache-0.2.0.tgz", + "integrity": "sha512-OcjA+jzjOYzKmKS6IQVALHLVz+rNTMPoJvCztFaZxwG14wtAW7VRZjwTQu06vKCYOxh4jVnik7ya0SXTB0W+xA==" }, - "merge-descriptors": { - "version": "1.0.1" + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" }, - "merge-stream": { + "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 }, - "merge2": { + "node_modules/merge2": { "version": "1.4.1", - "dev": true + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } }, - "methods": { - "version": "1.1.2" + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } }, - "micromatch": { + "node_modules/micromatch": { "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", "dev": true, - "requires": { + "dependencies": { "braces": "^3.0.2", "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" } }, - "mime": { - "version": "1.6.0" + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } }, - "mime-db": { - "version": "1.52.0" + "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" + } }, - "mime-types": { + "node_modules/mime-types": { "version": "2.1.35", - "requires": { + "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" } }, - "mimic-fn": { + "node_modules/mimic-fn": { "version": "2.1.0", - "dev": true + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } }, - "minimatch": { - "version": "3.1.2", - "requires": { - "brace-expansion": "^1.1.7" + "node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "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" } }, - "minimist": { - "version": "1.2.6" + "node_modules/minipass": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } }, - "mkdirp": { + "node_modules/mkdirp": { "version": "1.0.4", - "dev": true + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } }, - "module-details-from-path": { - "version": "1.0.3" + "node_modules/module-details-from-path": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz", + "integrity": "sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==" }, - "moment": { - "version": "2.29.4" + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "engines": { + "node": "*" + } }, - "morgan": { + "node_modules/morgan": { "version": "1.10.0", - "requires": { + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", + "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", + "dependencies": { "basic-auth": "~2.0.1", "debug": "2.6.9", "depd": "~2.0.0", "on-finished": "~2.3.0", "on-headers": "~1.0.2" }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/morgan/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dependencies": { - "debug": { - "version": "2.6.9", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0" - } + "ms": "2.0.0" + } + }, + "node_modules/morgan/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/morgan/node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" } }, - "ms": { - "version": "2.1.2" + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, - "mv": { + "node_modules/mv": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", + "integrity": "sha512-at/ZndSy3xEGJ8i0ygALh8ru9qy7gWW1cmkaqBN29JmMlIvM//MEO9y1sk/avxuwnPcfhkejkLsuPxH81BrkSg==", "optional": true, - "requires": { + "dependencies": { "mkdirp": "~0.5.1", "ncp": "~2.0.0", "rimraf": "~2.4.0" }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/mv/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "optional": true, "dependencies": { - "glob": { - "version": "6.0.4", - "optional": true, - "requires": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "2 || 3", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "mkdirp": { - "version": "0.5.6", - "optional": true, - "requires": { - "minimist": "^1.2.6" - } - }, - "rimraf": { - "version": "2.4.5", - "optional": true, - "requires": { - "glob": "^6.0.1" - } - } + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "nan": { - "version": "2.15.0", - "optional": true + "node_modules/mv/node_modules/glob": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", + "integrity": "sha512-MKZeRNyYZAVVVG1oZeLaWie1uweH40m9AZwIwxyPbTSX4hHrVYSzLg0Ro5Z5R7XKkIX+Cc6oD1rqeDJnwsB8/A==", + "optional": true, + "dependencies": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + } }, - "natural-compare": { - "version": "1.4.0", - "dev": true + "node_modules/mv/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "optional": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mv/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==", + "optional": true, + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mv/node_modules/rimraf": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", + "integrity": "sha512-J5xnxTyqaiw06JjMftq7L9ouA448dw/E7dKghkP9WpKNuwmARNNg+Gk8/u5ryb9N/Yo2+z3MCwuqFK/+qPOPfQ==", + "optional": true, + "dependencies": { + "glob": "^6.0.1" + }, + "bin": { + "rimraf": "bin.js" + } }, - "natural-compare-lite": { + "node_modules/nan": { + "version": "2.18.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.18.0.tgz", + "integrity": "sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w==", + "optional": true + }, + "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 }, - "ncp": { + "node_modules/ncp": { "version": "2.0.0", - "optional": true + "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", + "integrity": "sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA==", + "optional": true, + "bin": { + "ncp": "bin/ncp" + } }, - "negotiator": { - "version": "0.6.3" + "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-abort-controller": { - "version": "3.1.1" + "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==" }, - "node-fetch": { - "version": "2.6.11", - "requires": { + "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" }, - "dependencies": { - "tr46": { - "version": "0.0.3" - }, - "webidl-conversions": { - "version": "3.0.1" - }, - "whatwg-url": { - "version": "5.0.0", - "requires": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true } } }, - "node-forge": { - "version": "1.3.1" + "node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "engines": { + "node": ">= 6.13.0" + } }, - "node-int64": { + "node_modules/node-int64": { "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", "dev": true }, - "node-jose": { + "node_modules/node-jose": { "version": "2.2.0", - "requires": { + "resolved": "https://registry.npmjs.org/node-jose/-/node-jose-2.2.0.tgz", + "integrity": "sha512-XPCvJRr94SjLrSIm4pbYHKLEaOsDvJCpyFw/6V/KK/IXmyZ6SFBzAUDO9HQf4DB/nTEFcRGH87mNciOP23kFjw==", + "dependencies": { "base64url": "^3.0.1", "buffer": "^6.0.3", "es6-promise": "^4.2.8", @@ -14935,156 +10170,274 @@ "pako": "^2.0.4", "process": "^0.11.10", "uuid": "^9.0.0" - }, - "dependencies": { - "uuid": { - "version": "9.0.0" - } } }, - "node-releases": { - "version": "2.0.10", + "node_modules/node-jose/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/node-releases": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", "dev": true }, - "nodemailer": { - "version": "6.9.4", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.4.tgz", - "integrity": "sha512-CXjQvrQZV4+6X5wP6ZIgdehJamI63MFoYFGGPtHudWym9qaEHDNdPzaj5bfMCvxG1vhAileSWW90q7nL0N36mA==" + "node_modules/nodemailer": { + "version": "6.9.8", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.8.tgz", + "integrity": "sha512-cfrYUk16e67Ks051i4CntM9kshRYei1/o/Gi8K1d+R34OIs21xdFnW7Pt7EucmVKA0LKtqUGNcjMZ7ehjl49mQ==", + "engines": { + "node": ">=6.0.0" + } }, - "normalize-path": { + "node_modules/normalize-path": { "version": "3.0.0", - "dev": true + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "npm-run-path": { + "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, - "requires": { + "dependencies": { "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, - "oauth": { - "version": "0.9.15" + "node_modules/oauth": { + "version": "0.9.15", + "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", + "integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==" }, - "object-assign": { - "version": "4.1.1" + "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" + } }, - "object-inspect": { - "version": "1.12.3" + "node_modules/object-inspect": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "object-path": { - "version": "0.11.8" + "node_modules/object-path": { + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/object-path/-/object-path-0.11.8.tgz", + "integrity": "sha512-YJjNZrlXJFM42wTBn6zgOJVar9KFJvzx6sTWDte8sWZF//cnjl0BxHNpfZx+ZffXX63A9q0b1zsFiBX4g4X5KA==", + "engines": { + "node": ">= 10.12.0" + } }, - "obuf": { + "node_modules/obuf": { "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", "dev": true }, - "octicons": { - "version": "5.0.1" - }, - "on-finished": { - "version": "2.3.0", - "requires": { + "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" } }, - "on-headers": { - "version": "1.0.2" + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "engines": { + "node": ">= 0.8" + } }, - "once": { + "node_modules/once": { "version": "1.4.0", - "requires": { + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { "wrappy": "1" } }, - "onetime": { + "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, - "requires": { + "dependencies": { "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "open": { - "version": "8.4.0", - "requires": { + "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==", + "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" } }, - "optionator": { - "version": "0.9.1", + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", "dev": true, - "requires": { + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" } }, - "p-limit": { + "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, - "requires": { + "dependencies": { "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "p-locate": { + "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, - "requires": { + "dependencies": { "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "p-map": { - "version": "4.0.0", + "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==", "dev": true, - "requires": { - "aggregate-error": "^3.0.0" + "engines": { + "node": ">=6" } }, - "p-try": { - "version": "2.2.0", - "dev": true - }, - "packet-reader": { - "version": "1.0.0" + "node_modules/packet-reader": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", + "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" }, - "pako": { - "version": "2.0.4" + "node_modules/pako": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==" }, - "parent-module": { + "node_modules/parent-module": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-2.0.0.tgz", + "integrity": "sha512-uo0Z9JJeWzv8BG+tRcapBKNJ0dro9cLyczGzulS6EfeyAdeC9sbojtW6XwvYxJkEne9En+J2XEl4zyglVeIwFg==", "dev": true, - "requires": { + "dependencies": { "callsites": "^3.1.0" + }, + "engines": { + "node": ">=8" } }, - "parse-json": { + "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, - "requires": { + "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" } }, - "parseurl": { - "version": "1.3.3" + "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" + } }, - "passport": { - "version": "0.6.0", - "requires": { + "node_modules/passport": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/passport/-/passport-0.7.0.tgz", + "integrity": "sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==", + "dependencies": { "passport-strategy": "1.x.x", "pause": "0.0.1", "utils-merge": "^1.0.1" + }, + "engines": { + "node": ">= 0.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jaredhanson" } }, - "passport-azure-ad": { + "node_modules/passport-azure-ad": { "version": "4.3.5", - "requires": { + "resolved": "https://registry.npmjs.org/passport-azure-ad/-/passport-azure-ad-4.3.5.tgz", + "integrity": "sha512-LBpXEght7hCMuMNFK4oegdN0uPBa3lpDMy71zQoB0zPg1RrGwdzpjwTiN1WzN0hY77fLyjz9tBr3TGAxnSgtEg==", + "dependencies": { "async": "^3.2.3", "base64url": "^3.0.0", "bunyan": "^1.8.14", @@ -15097,266 +10450,565 @@ "passport": "^0.6.0", "valid-url": "^1.0.6" }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/passport-azure-ad/node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", "dependencies": { - "async": { - "version": "3.2.4" - }, - "jwa": { - "version": "1.4.1", - "requires": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "jws": { - "version": "3.2.2", - "requires": { - "jwa": "^1.4.1", - "safe-buffer": "^5.0.1" - } - } + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/passport-azure-ad/node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/passport-azure-ad/node_modules/passport": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/passport/-/passport-0.6.0.tgz", + "integrity": "sha512-0fe+p3ZnrWRW74fe8+SvCyf4a3Pb2/h7gFkQ8yTJpAO50gDzlfjZUZTO1k5Eg9kUct22OxHLqDZoKUWRHOh9ug==", + "dependencies": { + "passport-strategy": "1.x.x", + "pause": "0.0.1", + "utils-merge": "^1.0.1" + }, + "engines": { + "node": ">= 0.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jaredhanson" } }, - "passport-github": { + "node_modules/passport-github": { "version": "1.1.0", - "requires": { + "resolved": "https://registry.npmjs.org/passport-github/-/passport-github-1.1.0.tgz", + "integrity": "sha512-XARXJycE6fFh/dxF+Uut8OjlwbFEXgbPVj/+V+K7cvriRK7VcAOm+NgBmbiLM9Qv3SSxEAV+V6fIk89nYHXa8A==", + "dependencies": { "passport-oauth2": "1.x.x" + }, + "engines": { + "node": ">= 0.4.0" } }, - "passport-oauth2": { - "version": "1.6.1", - "requires": { + "node_modules/passport-oauth2": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.7.0.tgz", + "integrity": "sha512-j2gf34szdTF2Onw3+76alNnaAExlUmHvkc7cL+cmaS5NzHzDP/BvFHJruueQ9XAeNOdpI+CH+PWid8RA7KCwAQ==", + "dependencies": { "base64url": "3.x.x", "oauth": "0.9.x", "passport-strategy": "1.x.x", "uid2": "0.0.x", "utils-merge": "1.x.x" + }, + "engines": { + "node": ">= 0.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jaredhanson" } }, - "passport-strategy": { - "version": "1.0.0" + "node_modules/passport-strategy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", + "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==", + "engines": { + "node": ">= 0.4.0" + } }, - "path-browserify": { + "node_modules/path-browserify": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", "dev": true }, - "path-exists": { + "node_modules/path-exists": { "version": "4.0.0", - "dev": true + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } }, - "path-is-absolute": { - "version": "1.0.1" + "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" + } }, - "path-key": { + "node_modules/path-key": { "version": "3.1.1", - "dev": true + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } }, - "path-parse": { - "version": "1.0.7" + "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.10.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", + "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", + "dev": true, + "dependencies": { + "lru-cache": "^9.1.1 || ^10.0.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, - "path-to-regexp": { - "version": "0.1.7" + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" }, - "path-type": { + "node_modules/path-type": { "version": "4.0.0", - "dev": true + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } }, - "pause": { - "version": "0.0.1" + "node_modules/pause": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", + "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==" }, - "pg": { - "version": "8.11.0", - "requires": { + "node_modules/pg": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.3.tgz", + "integrity": "sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g==", + "dependencies": { "buffer-writer": "2.0.0", "packet-reader": "1.0.0", - "pg-cloudflare": "^1.1.0", - "pg-connection-string": "^2.6.0", - "pg-pool": "^3.6.0", + "pg-connection-string": "^2.6.2", + "pg-pool": "^3.6.1", "pg-protocol": "^1.6.0", "pg-types": "^2.1.0", "pgpass": "1.x" + }, + "engines": { + "node": ">= 8.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.1.1" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } } }, - "pg-cloudflare": { - "version": "1.1.0", + "node_modules/pg-cloudflare": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz", + "integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==", "optional": true }, - "pg-connection-string": { - "version": "2.6.0" + "node_modules/pg-connection-string": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz", + "integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==" + }, + "node_modules/pg-escape": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/pg-escape/-/pg-escape-0.2.0.tgz", + "integrity": "sha512-QfXcpxyN9vT+kvexQpTeNyiwCxs4LPXLAV/C0EeLTJPCF61swhGdDlnHcuDgxJudgKGfQYiCkegg0GsSKSvvEg==" }, - "pg-escape": { - "version": "0.2.0" + "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" + } }, - "pg-int8": { - "version": "1.0.1" + "node_modules/pg-numeric": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pg-numeric/-/pg-numeric-1.0.2.tgz", + "integrity": "sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==", + "dev": true, + "engines": { + "node": ">=4" + } }, - "pg-numeric": { - "version": "1.0.2", - "dev": true + "node_modules/pg-pool": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.1.tgz", + "integrity": "sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og==", + "peerDependencies": { + "pg": ">=8.0" + } }, - "pg-pool": { - "version": "3.6.0", - "requires": {} + "node_modules/pg-protocol": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz", + "integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==" }, - "pg-protocol": { - "version": "1.6.0" + "node_modules/pg-types": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-4.0.1.tgz", + "integrity": "sha512-hRCSDuLII9/LE3smys1hRHcu5QGcLs9ggT7I/TCs0IE+2Eesxi9+9RWAAwZ0yaGjxoWICF/YHLOEjydGujoJ+g==", + "dev": true, + "dependencies": { + "pg-int8": "1.0.1", + "pg-numeric": "1.0.2", + "postgres-array": "~3.0.1", + "postgres-bytea": "~3.0.0", + "postgres-date": "~2.0.1", + "postgres-interval": "^3.0.0", + "postgres-range": "^1.1.1" + }, + "engines": { + "node": ">=10" + } }, - "pg-types": { + "node_modules/pg/node_modules/pg-types": { "version": "2.2.0", - "requires": { + "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/pg/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/pg/node_modules/postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pg/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/pg/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" } }, - "pgpass": { + "node_modules/pgpass": { "version": "1.0.5", - "requires": { + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "dependencies": { "split2": "^4.1.0" } }, - "picocolors": { + "node_modules/picocolors": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", "dev": true }, - "picomatch": { + "node_modules/picomatch": { "version": "2.3.1", - "dev": true + "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" + } }, - "pidtree": { + "node_modules/pidtree": { "version": "0.6.0", - "dev": true + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", + "dev": true, + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } }, - "pirates": { - "version": "4.0.5", - "dev": true + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "dev": true, + "engines": { + "node": ">= 6" + } }, - "pkg-dir": { + "node_modules/pkg-dir": { "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, - "requires": { + "dependencies": { "find-up": "^4.0.0" }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/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==", + "dev": true, "dependencies": { - "find-up": { - "version": "4.1.0", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-limit": { - "version": "2.3.0", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - } + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/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==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/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==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/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==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" } }, - "postgres-array": { - "version": "2.0.0" + "node_modules/postgres-array": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-3.0.2.tgz", + "integrity": "sha512-6faShkdFugNQCLwucjPcY5ARoW1SlbnrZjmGl0IrrqewpvxvhSLHimCVzqeuULCbG0fQv7Dtk1yDbG3xv7Veog==", + "dev": true, + "engines": { + "node": ">=12" + } }, - "postgres-bytea": { - "version": "1.0.0" + "node_modules/postgres-bytea": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-3.0.0.tgz", + "integrity": "sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==", + "dev": true, + "dependencies": { + "obuf": "~1.1.2" + }, + "engines": { + "node": ">= 6" + } }, - "postgres-date": { - "version": "1.0.7" + "node_modules/postgres-date": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-2.0.1.tgz", + "integrity": "sha512-YtMKdsDt5Ojv1wQRvUhnyDJNSr2dGIC96mQVKz7xufp07nfuFONzdaowrMHjlAzY6GDLd4f+LUHHAAM1h4MdUw==", + "dev": true, + "engines": { + "node": ">=12" + } }, - "postgres-interval": { - "version": "1.2.0", - "requires": { - "xtend": "^4.0.0" + "node_modules/postgres-interval": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-3.0.0.tgz", + "integrity": "sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==", + "dev": true, + "engines": { + "node": ">=12" } }, - "postgres-range": { + "node_modules/postgres-range": { "version": "1.1.3", + "resolved": "https://registry.npmjs.org/postgres-range/-/postgres-range-1.1.3.tgz", + "integrity": "sha512-VdlZoocy5lCP0c/t66xAfclglEapXPCIVhqqJRncYpvbCgImF0w67aPKfbqUMr72tO2k5q0TdTZwCLjPTI6C9g==", "dev": true }, - "prelude-ls": { + "node_modules/prelude-ls": { "version": "1.2.1", - "dev": true + "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" + } }, - "prettier": { - "version": "2.8.8", - "dev": true + "node_modules/prettier": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.1.tgz", + "integrity": "sha512-22UbSzg8luF4UuZtzgiUOfcGM8s4tjBv6dJRT7j275NXsy2jb4aJa4NNveul5x4eqlF1wuhuR2RElK71RvmVaw==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } }, - "prettier-linter-helpers": { + "node_modules/prettier-linter-helpers": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", "dev": true, - "requires": { + "dependencies": { "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" } }, - "pretty-format": { - "version": "29.5.0", + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, - "requires": { - "@jest/schemas": "^29.4.3", + "dependencies": { + "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" }, - "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "dev": true - } + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "priorityqueuejs": { - "version": "1.0.0" + "node_modules/priorityqueuejs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/priorityqueuejs/-/priorityqueuejs-1.0.0.tgz", + "integrity": "sha512-lg++21mreCEOuGWTbO5DnJKAdxfjrdN0S9ysoW9SzdSJvbkWpkaDdpG/cdsPCsEnoLUwmd9m3WcZhngW7yKA2g==" }, - "process": { - "version": "0.11.10" + "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==", + "engines": { + "node": ">= 0.6.0" + } }, - "promise": { + "node_modules/promise": { "version": "7.3.1", - "requires": { + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "dependencies": { "asap": "~2.0.3" } }, - "prompts": { + "node_modules/prompts": { "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", "dev": true, - "requires": { + "dependencies": { "kleur": "^3.0.3", "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" } }, - "proxy-addr": { + "node_modules/proxy-addr": { "version": "2.0.7", - "requires": { + "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" } }, - "proxy-from-env": { - "version": "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==" }, - "pseudomap": { - "version": "1.0.2" + "node_modules/pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==" }, - "pug": { + "node_modules/pug": { "version": "3.0.2", - "requires": { + "resolved": "https://registry.npmjs.org/pug/-/pug-3.0.2.tgz", + "integrity": "sha512-bp0I/hiK1D1vChHh6EfDxtndHji55XP/ZJKwsRqrz6lRia6ZC2OZbdAymlxdVFwd1L70ebrVJw4/eZ79skrIaw==", + "dependencies": { "pug-code-gen": "^3.0.2", "pug-filters": "^4.0.0", "pug-lexer": "^5.0.1", @@ -15367,17 +11019,21 @@ "pug-strip-comments": "^2.0.0" } }, - "pug-attrs": { + "node_modules/pug-attrs": { "version": "3.0.0", - "requires": { + "resolved": "https://registry.npmjs.org/pug-attrs/-/pug-attrs-3.0.0.tgz", + "integrity": "sha512-azINV9dUtzPMFQktvTXciNAfAuVh/L/JCl0vtPCwvOA21uZrC08K/UnmrL+SXGEVc1FwzjW62+xw5S/uaLj6cA==", + "dependencies": { "constantinople": "^4.0.1", "js-stringify": "^1.0.2", "pug-runtime": "^3.0.0" } }, - "pug-code-gen": { + "node_modules/pug-code-gen": { "version": "3.0.2", - "requires": { + "resolved": "https://registry.npmjs.org/pug-code-gen/-/pug-code-gen-3.0.2.tgz", + "integrity": "sha512-nJMhW16MbiGRiyR4miDTQMRWDgKplnHyeLvioEJYbk1RsPI3FuA3saEP8uwnTb2nTJEKBU90NFVWJBk4OU5qyg==", + "dependencies": { "constantinople": "^4.0.1", "doctypes": "^1.1.0", "js-stringify": "^1.0.2", @@ -15388,12 +11044,16 @@ "with": "^7.0.0" } }, - "pug-error": { - "version": "2.0.0" + "node_modules/pug-error": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pug-error/-/pug-error-2.0.0.tgz", + "integrity": "sha512-sjiUsi9M4RAGHktC1drQfCr5C5eriu24Lfbt4s+7SykztEOwVZtbFk1RRq0tzLxcMxMYTBR+zMQaG07J/btayQ==" }, - "pug-filters": { + "node_modules/pug-filters": { "version": "4.0.0", - "requires": { + "resolved": "https://registry.npmjs.org/pug-filters/-/pug-filters-4.0.0.tgz", + "integrity": "sha512-yeNFtq5Yxmfz0f9z2rMXGw/8/4i1cCFecw/Q7+D0V2DdtII5UvqE12VaZ2AY7ri6o5RNXiweGH79OCq+2RQU4A==", + "dependencies": { "constantinople": "^4.0.1", "jstransformer": "1.0.0", "pug-error": "^2.0.0", @@ -15401,235 +11061,480 @@ "resolve": "^1.15.1" } }, - "pug-lexer": { + "node_modules/pug-lexer": { "version": "5.0.1", - "requires": { + "resolved": "https://registry.npmjs.org/pug-lexer/-/pug-lexer-5.0.1.tgz", + "integrity": "sha512-0I6C62+keXlZPZkOJeVam9aBLVP2EnbeDw3An+k0/QlqdwH6rv8284nko14Na7c0TtqtogfWXcRoFE4O4Ff20w==", + "dependencies": { "character-parser": "^2.2.0", "is-expression": "^4.0.0", "pug-error": "^2.0.0" } }, - "pug-linker": { + "node_modules/pug-linker": { "version": "4.0.0", - "requires": { + "resolved": "https://registry.npmjs.org/pug-linker/-/pug-linker-4.0.0.tgz", + "integrity": "sha512-gjD1yzp0yxbQqnzBAdlhbgoJL5qIFJw78juN1NpTLt/mfPJ5VgC4BvkoD3G23qKzJtIIXBbcCt6FioLSFLOHdw==", + "dependencies": { "pug-error": "^2.0.0", "pug-walk": "^2.0.0" } }, - "pug-load": { + "node_modules/pug-load": { "version": "3.0.0", - "requires": { + "resolved": "https://registry.npmjs.org/pug-load/-/pug-load-3.0.0.tgz", + "integrity": "sha512-OCjTEnhLWZBvS4zni/WUMjH2YSUosnsmjGBB1An7CsKQarYSWQ0GCVyd4eQPMFJqZ8w9xgs01QdiZXKVjk92EQ==", + "dependencies": { "object-assign": "^4.1.1", "pug-walk": "^2.0.0" } }, - "pug-parser": { + "node_modules/pug-parser": { "version": "6.0.0", - "requires": { + "resolved": "https://registry.npmjs.org/pug-parser/-/pug-parser-6.0.0.tgz", + "integrity": "sha512-ukiYM/9cH6Cml+AOl5kETtM9NR3WulyVP2y4HOU45DyMim1IeP/OOiyEWRr6qk5I5klpsBnbuHpwKmTx6WURnw==", + "dependencies": { "pug-error": "^2.0.0", "token-stream": "1.0.0" } }, - "pug-runtime": { - "version": "3.0.1" + "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==" }, - "pug-strip-comments": { + "node_modules/pug-strip-comments": { "version": "2.0.0", - "requires": { + "resolved": "https://registry.npmjs.org/pug-strip-comments/-/pug-strip-comments-2.0.0.tgz", + "integrity": "sha512-zo8DsDpH7eTkPHCXFeAk1xZXJbyoTfdPlNR0bK7rpOMuhBYb0f5qUVCO1xlsitYd3w5FQTK7zpNVKb3rZoUrrQ==", + "dependencies": { "pug-error": "^2.0.0" } }, - "pug-walk": { - "version": "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==" }, - "punycode": { - "version": "2.1.1", - "dev": true + "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" + } }, - "pure-rand": { - "version": "6.0.2", - "dev": true + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/pure-rand": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.4.tgz", + "integrity": "sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ] }, - "qs": { + "node_modules/qs": { "version": "6.11.0", - "requires": { + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": { "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "queue-microtask": { + "node_modules/queue-microtask": { "version": "1.2.3", - "dev": true + "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" + } + ] }, - "random-bytes": { - "version": "1.0.0" + "node_modules/random-bytes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", + "integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==", + "engines": { + "node": ">= 0.8" + } }, - "range-parser": { - "version": "1.2.1" + "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" + } }, - "raw-body": { + "node_modules/raw-body": { "version": "2.5.2", - "requires": { + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" } }, - "react-is": { + "node_modules/react-is": { "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", "dev": true }, - "recursive-readdir": { + "node_modules/recursive-readdir": { "version": "2.2.3", - "requires": { + "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", + "integrity": "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==", + "dependencies": { "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/recursive-readdir/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/recursive-readdir/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": "*" } }, - "redis": { - "version": "4.6.8", - "resolved": "https://registry.npmjs.org/redis/-/redis-4.6.8.tgz", - "integrity": "sha512-S7qNkPUYrsofQ0ztWlTHSaK0Qqfl1y+WMIxrzeAGNG+9iUZB4HGeBgkHxE6uJJ6iXrkvLd1RVJ2nvu6H1sAzfQ==", - "requires": { + "node_modules/redis": { + "version": "4.6.12", + "resolved": "https://registry.npmjs.org/redis/-/redis-4.6.12.tgz", + "integrity": "sha512-41Xuuko6P4uH4VPe5nE3BqXHB7a9lkFL0J29AlxKaIfD6eWO8VO/5PDF9ad2oS+mswMsfFxaM5DlE3tnXT+P8Q==", + "dependencies": { "@redis/bloom": "1.2.0", - "@redis/client": "1.5.9", - "@redis/graph": "1.1.0", - "@redis/json": "1.0.4", - "@redis/search": "1.1.3", + "@redis/client": "1.5.13", + "@redis/graph": "1.1.1", + "@redis/json": "1.0.6", + "@redis/search": "1.1.6", "@redis/time-series": "1.0.5" } }, - "repeat-string": { + "node_modules/repeat-string": { "version": "1.6.1", - "dev": true + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", + "dev": true, + "engines": { + "node": ">=0.10" + } }, - "require-directory": { + "node_modules/require-directory": { "version": "2.1.1", - "dev": true + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "require-in-the-middle": { - "version": "5.2.0", - "requires": { + "node_modules/require-in-the-middle": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-7.2.0.tgz", + "integrity": "sha512-3TLx5TGyAY6AOqLBoXmHkNql0HIf2RGbuMgCDT2WO/uGVAPJs6h7Kl+bN6TIZGd9bWhWPwnDnTHGtW8Iu77sdw==", + "dependencies": { "debug": "^4.1.1", "module-details-from-path": "^1.0.3", "resolve": "^1.22.1" + }, + "engines": { + "node": ">=8.6.0" } }, - "resolve": { - "version": "1.22.2", - "requires": { - "is-core-module": "^2.11.0", + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "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" } }, - "resolve-cwd": { + "node_modules/resolve-cwd": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", "dev": true, - "requires": { + "dependencies": { "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" } }, - "resolve-from": { + "node_modules/resolve-from": { "version": "5.0.0", - "dev": true + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } }, - "resolve-global": { + "node_modules/resolve-pkg-maps": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", "dev": true, - "requires": { - "global-dirs": "^0.1.1" + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, - "resolve.exports": { + "node_modules/resolve.exports": { "version": "2.0.2", - "dev": true + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", + "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", + "dev": true, + "engines": { + "node": ">=10" + } }, - "restore-cursor": { - "version": "3.1.0", + "node_modules/restore-cursor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", + "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", "dev": true, - "requires": { + "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "reusify": { + "node_modules/reusify": { "version": "1.0.4", - "dev": true + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } }, - "rfdc": { - "version": "1.3.0", + "node_modules/rfdc": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.1.tgz", + "integrity": "sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==", "dev": true }, - "rhea": { + "node_modules/rhea": { "version": "3.0.2", - "requires": { + "resolved": "https://registry.npmjs.org/rhea/-/rhea-3.0.2.tgz", + "integrity": "sha512-0G1ZNM9yWin8VLvTxyISKH6KfR6gl1TW/1+5yMKPf2r1efhkzTLze09iFtT2vpDjuWIVtSmXz8r18lk/dO8qwQ==", + "dependencies": { "debug": "^4.3.3" } }, - "rhea-promise": { + "node_modules/rhea-promise": { "version": "3.0.1", - "requires": { + "resolved": "https://registry.npmjs.org/rhea-promise/-/rhea-promise-3.0.1.tgz", + "integrity": "sha512-Fcqgml7lgoyi7fH1ClsSyFr/xwToijEN3rULFgrIcL+7EHeduxkWogFxNHjFzHf2YGScAckJDaDxS1RdlTUQYw==", + "dependencies": { "debug": "^3.1.0", "rhea": "^3.0.0", "tslib": "^2.2.0" - }, + } + }, + "node_modules/rhea-promise/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dependencies": { - "debug": { - "version": "3.2.7", - "requires": { - "ms": "^2.1.1" - } - } + "ms": "^2.1.1" } }, - "rimraf": { - "version": "3.0.2", - "requires": { - "glob": "^7.1.3" + "node_modules/rimraf": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.5.tgz", + "integrity": "sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==", + "dev": true, + "dependencies": { + "glob": "^10.3.7" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "run-parallel": { - "version": "1.2.0", + "node_modules/rimraf/node_modules/glob": { + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", "dev": true, - "requires": { - "queue-microtask": "^1.2.2" + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "rxjs": { - "version": "7.8.1", + "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, - "requires": { - "tslib": "^2.1.0" + "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" } }, - "safe-buffer": { - "version": "5.1.2" + "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==" }, - "safe-json-stringify": { + "node_modules/safe-json-stringify": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz", + "integrity": "sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==", "optional": true }, - "safer-buffer": { - "version": "2.1.2" + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, - "sax": { - "version": "1.2.4" + "node_modules/sax": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz", + "integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==" }, - "secure-compare": { - "version": "3.0.1" + "node_modules/secure-compare": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz", + "integrity": "sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==" }, - "semaphore": { - "version": "1.1.0" + "node_modules/semaphore": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/semaphore/-/semaphore-1.1.0.tgz", + "integrity": "sha512-O4OZEaNtkMd/K0i6js9SL+gqy0ZCBMgUvlSqHKi4IBdjhe7wB8pwztUk1BbZ1fmrvpwFrPbHzqd2w5pTcJH6LA==", + "engines": { + "node": ">=0.8.0" + } }, - "semver": { - "version": "7.5.1", - "requires": { + "node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dependencies": { "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" } }, - "send": { + "node_modules/send": { "version": "0.18.0", - "requires": { + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dependencies": { "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", @@ -15644,294 +11549,686 @@ "range-parser": "~1.2.1", "statuses": "2.0.1" }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dependencies": { - "debug": { - "version": "2.6.9", - "requires": { - "ms": "2.0.0" - }, - "dependencies": { - "ms": { - "version": "2.0.0" - } - } - }, - "ms": { - "version": "2.1.3" - }, - "on-finished": { - "version": "2.4.1", - "requires": { - "ee-first": "1.1.1" - } - } + "ms": "2.0.0" } }, - "serve-favicon": { + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/serve-favicon": { "version": "2.5.0", - "requires": { + "resolved": "https://registry.npmjs.org/serve-favicon/-/serve-favicon-2.5.0.tgz", + "integrity": "sha512-FMW2RvqNr03x+C0WxTyu6sOv21oOjkq5j8tjquWccwa6ScNyGFOGJVpuS1NmTVGBAHS07xnSKotgf2ehQmf9iA==", + "dependencies": { "etag": "~1.8.1", "fresh": "0.5.2", "ms": "2.1.1", "parseurl": "~1.3.2", "safe-buffer": "5.1.1" }, - "dependencies": { - "ms": { - "version": "2.1.1" - }, - "safe-buffer": { - "version": "5.1.1" - } + "engines": { + "node": ">= 0.8.0" } }, - "serve-static": { + "node_modules/serve-favicon/node_modules/ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + }, + "node_modules/serve-favicon/node_modules/safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" + }, + "node_modules/serve-static": { "version": "1.15.0", - "requires": { + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dependencies": { "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "parseurl": "~1.3.3", "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-function-length": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.0.tgz", + "integrity": "sha512-4DBHDoyHlM1IRPGYcoxexgh67y4ueR53FKV1yyxwFMY7aCqcN/38M1+SwZ/qJQ8iLv7+ck385ot4CcisOAPT9w==", + "dependencies": { + "define-data-property": "^1.1.1", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.2", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" } }, - "setprototypeof": { - "version": "1.2.0" + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, - "shebang-command": { + "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, - "requires": { + "dependencies": { "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, - "shebang-regex": { + "node_modules/shebang-regex": { "version": "3.0.0", - "dev": true + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } }, - "shimmer": { - "version": "1.2.1" + "node_modules/shimmer": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz", + "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==" }, - "side-channel": { + "node_modules/side-channel": { "version": "1.0.4", - "requires": { + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dependencies": { "call-bind": "^1.0.0", "get-intrinsic": "^1.0.2", "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "signal-exit": { + "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 }, - "simple-oauth2": { + "node_modules/simple-oauth2": { "version": "5.0.0", - "requires": { + "resolved": "https://registry.npmjs.org/simple-oauth2/-/simple-oauth2-5.0.0.tgz", + "integrity": "sha512-8291lo/z5ZdpmiOFzOs1kF3cxn22bMj5FFH+DNUppLJrpoIlM1QnFiE7KpshHu3J3i21TVcx4yW+gXYjdCKDLQ==", + "dependencies": { "@hapi/hoek": "^10.0.1", "@hapi/wreck": "^18.0.0", "debug": "^4.3.4", "joi": "^17.6.4" } }, - "sisteransi": { + "node_modules/sisteransi": { "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", "dev": true }, - "slash": { + "node_modules/slash": { "version": "3.0.0", - "dev": true + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } }, - "slice-ansi": { + "node_modules/slice-ansi": { "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", "dev": true, - "requires": { + "dependencies": { "ansi-styles": "^6.0.0", "is-fullwidth-code-point": "^4.0.0" }, - "dependencies": { - "ansi-styles": { - "version": "6.2.1", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "4.0.0", - "dev": true - } + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/slice-ansi/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" } }, - "source-map": { + "node_modules/source-map": { "version": "0.6.1", - "dev": true + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "source-map-support": { + "node_modules/source-map-support": { "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", "dev": true, - "requires": { + "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" } }, - "split2": { - "version": "4.1.0" + "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" + } }, - "sprintf-js": { + "node_modules/sprintf-js": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, - "stack-chain": { - "version": "1.3.7" + "node_modules/stack-chain": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/stack-chain/-/stack-chain-1.3.7.tgz", + "integrity": "sha512-D8cWtWVdIe/jBA7v5p5Hwl5yOSOrmZPWDPe2KxQ5UAGD+nxbxU0lKXA4h85Ta6+qgdKVL3vUxsbIZjc1kBG7ug==" }, - "stack-utils": { + "node_modules/stack-utils": { "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", "dev": true, - "requires": { + "dependencies": { "escape-string-regexp": "^2.0.0" }, - "dependencies": { - "escape-string-regexp": { - "version": "2.0.0", - "dev": true - } + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "engines": { + "node": ">=8" } }, - "statuses": { - "version": "2.0.1" + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } }, - "stoppable": { - "version": "1.1.0" + "node_modules/stoppable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz", + "integrity": "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==", + "engines": { + "node": ">=4", + "npm": ">=6" + } }, - "string-argv": { + "node_modules/string-argv": { "version": "0.3.2", - "dev": true + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "dev": true, + "engines": { + "node": ">=0.6.19" + } }, - "string-length": { + "node_modules/string-length": { "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", "dev": true, - "requires": { + "dependencies": { "char-regex": "^1.0.2", "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-length/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/string-length/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/string-width": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.0.0.tgz", + "integrity": "sha512-GPQHj7row82Hjo9hKZieKcHIhaAIKOJvFSIZXuCU9OASVZrMNUaZuz++SPVrBjnLsnk4k+z9f2EIypgxf2vNFw==", + "dev": true, + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "string-width": { + "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, - "requires": { + "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==", + "dev": true, + "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-cjs/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==", + "dev": true, + "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==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "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" } }, - "strip-ansi": { + "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, - "requires": { + "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==", + "dev": true, + "engines": { + "node": ">=8" } }, - "strip-bom": { - "version": "4.0.0" + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "engines": { + "node": ">=8" + } }, - "strip-final-newline": { + "node_modules/strip-final-newline": { "version": "2.0.0", - "dev": true + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } }, - "strip-json-comments": { - "version": "3.1.1" + "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==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "strnum": { - "version": "1.0.5" + "node_modules/strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==" }, - "supports-color": { + "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, - "requires": { + "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" } }, - "supports-preserve-symlinks-flag": { - "version": "1.0.0" + "node_modules/synckit": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.8.tgz", + "integrity": "sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==", + "dev": true, + "dependencies": { + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } }, - "test-exclude": { + "node_modules/test-exclude": { "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", "dev": true, - "requires": { + "dependencies": { "@istanbuljs/schema": "^0.1.2", "glob": "^7.1.4", "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" } }, - "text-table": { - "version": "0.2.0", - "dev": true + "node_modules/test-exclude/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } }, - "throat": { - "version": "6.0.2" + "node_modules/test-exclude/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": "*" + } }, - "through": { - "version": "2.3.8", + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, - "tmp": { + "node_modules/throat": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.2.tgz", + "integrity": "sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ==" + }, + "node_modules/tmp": { "version": "0.2.1", - "requires": { + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dependencies": { "rimraf": "^3.0.0" + }, + "engines": { + "node": ">=8.17.0" } }, - "tmp-promise": { + "node_modules/tmp-promise": { "version": "3.0.3", - "requires": { + "resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-3.0.3.tgz", + "integrity": "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==", + "dependencies": { "tmp": "^0.2.0" } }, - "tmpl": { + "node_modules/tmp/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/tmpl": { "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", "dev": true }, - "to-fast-properties": { - "version": "2.0.0" + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "engines": { + "node": ">=4" + } }, - "to-regex-range": { + "node_modules/to-regex-range": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, - "requires": { + "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" } }, - "toidentifier": { - "version": "1.0.1" + "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==" }, - "token-stream": { - "version": "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==" }, - "true-myth": { + "node_modules/true-myth": { "version": "4.1.1", - "dev": true + "resolved": "https://registry.npmjs.org/true-myth/-/true-myth-4.1.1.tgz", + "integrity": "sha512-rqy30BSpxPznbbTcAcci90oZ1YR4DqvKcNXNerG5gQBU2v4jk0cygheiul5J6ExIMrgDVuanv/MkGfqZbKrNNg==", + "dev": true, + "engines": { + "node": "10.* || >= 12.*" + } + }, + "node_modules/ts-api-utils": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", + "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", + "dev": true, + "engines": { + "node": ">=16.13.0" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } }, - "ts-jest": { - "version": "29.1.0", + "node_modules/ts-jest": { + "version": "29.1.1", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.1.tgz", + "integrity": "sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA==", "dev": true, - "requires": { + "dependencies": { "bs-logger": "0.x", "fast-json-stable-stringify": "2.x", "jest-util": "^29.0.0", "json5": "^2.2.3", "lodash.memoize": "4.x", "make-error": "1.x", - "semver": "7.x", + "semver": "^7.5.3", "yargs-parser": "^21.0.1" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/types": "^29.0.0", + "babel-jest": "^29.0.0", + "jest": "^29.0.0", + "typescript": ">=4.3 <6" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + } } }, - "ts-morph": { + "node_modules/ts-morph": { "version": "13.0.3", + "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-13.0.3.tgz", + "integrity": "sha512-pSOfUMx8Ld/WUreoSzvMFQG5i9uEiWIsBYjpU9+TTASOeUa89j5HykomeqVULm1oqWtBdleI3KEFRLrlA3zGIw==", "dev": true, - "requires": { + "dependencies": { "@ts-morph/common": "~0.12.3", "code-block-writer": "^11.0.0" } }, - "ts-node": { - "version": "10.9.1", + "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==", "dev": true, - "requires": { + "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", "@tsconfig/node12": "^1.0.7", @@ -15945,12 +12242,36 @@ "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 + } } }, - "ts-prune": { + "node_modules/ts-prune": { "version": "0.10.3", + "resolved": "https://registry.npmjs.org/ts-prune/-/ts-prune-0.10.3.tgz", + "integrity": "sha512-iS47YTbdIcvN8Nh/1BFyziyUqmjXz7GVzWu02RaZXqb+e/3Qe1B7IQ4860krOeCGUeJmterAlaM2FRH0Ue0hjw==", "dev": true, - "requires": { + "dependencies": { "commander": "^6.2.1", "cosmiconfig": "^7.0.1", "json5": "^2.1.3", @@ -15958,131 +12279,219 @@ "true-myth": "^4.1.0", "ts-morph": "^13.0.1" }, - "dependencies": { - "commander": { - "version": "6.2.1", - "dev": true - }, - "cosmiconfig": { - "version": "7.1.0", - "dev": true, - "requires": { - "@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" - } - }, - "yaml": { - "version": "1.10.2", - "dev": true - } + "bin": { + "ts-prune": "lib/index.js" } }, - "tslib": { - "version": "2.3.1" - }, - "tsutils": { - "version": "3.21.0", + "node_modules/ts-prune/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, - "requires": { - "tslib": "^1.8.1" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "dev": true - } + "engines": { + "node": ">= 6" } }, - "tunnel": { - "version": "0.0.6" + "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/tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "engines": { + "node": ">=0.6.11 <=0.7.0 || >=0.7.3" + } }, - "type-check": { + "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, - "requires": { + "dependencies": { "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" } }, - "type-detect": { + "node_modules/type-detect": { "version": "4.0.8", - "dev": true + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } }, - "type-fest": { + "node_modules/type-fest": { "version": "0.20.2", - "dev": true + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "type-is": { + "node_modules/type-is": { "version": "1.6.18", - "requires": { + "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" } }, - "typedarray-to-buffer": { + "node_modules/typedarray-to-buffer": { "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", "dev": true, - "requires": { + "dependencies": { "is-typedarray": "^1.0.0" } }, - "typescript": { - "version": "5.0.4", - "dev": true + "node_modules/typescript": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } }, - "uc.micro": { - "version": "1.0.6", + "node_modules/uc.micro": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.0.0.tgz", + "integrity": "sha512-DffL94LsNOccVn4hyfRe5rdKa273swqeA5DJpMOeFmEn1wCDc7nAbbB0gXlgBCL7TNzeTv6G7XVWzan7iJtfig==", "dev": true }, - "uid-safe": { + "node_modules/uid-safe": { "version": "2.1.5", - "requires": { + "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", + "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", + "dependencies": { "random-bytes": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" } }, - "uid2": { - "version": "0.0.4" + "node_modules/uid2": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.4.tgz", + "integrity": "sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA==" + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" }, - "unique-string": { - "version": "2.0.0", + "node_modules/unicorn-magic": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", + "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", "dev": true, - "requires": { - "crypto-random-string": "^2.0.0" + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "universal-github-app-jwt": { - "version": "1.1.1", - "requires": { + "node_modules/unique-string": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-3.0.0.tgz", + "integrity": "sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==", + "dev": true, + "dependencies": { + "crypto-random-string": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/universal-github-app-jwt": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/universal-github-app-jwt/-/universal-github-app-jwt-1.1.2.tgz", + "integrity": "sha512-t1iB2FmLFE+yyJY9+3wMx0ejB+MQpEVkH0gQv7dR6FZyltyq+ZZO0uDpbopxhrZ3SLEO4dCEkIujOMldEQ2iOA==", + "dependencies": { "@types/jsonwebtoken": "^9.0.0", - "jsonwebtoken": "^9.0.0" + "jsonwebtoken": "^9.0.2" } }, - "universal-user-agent": { - "version": "6.0.0" + "node_modules/universal-user-agent": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz", + "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==" }, - "unpipe": { - "version": "1.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" + } }, - "update-browserslist-db": { - "version": "1.0.11", + "node_modules/update-browserslist-db": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", "dev": true, - "requires": { + "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.1.1", "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" } }, - "uri-js": { + "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, - "requires": { + "dependencies": { "punycode": "^2.1.0" } }, - "util": { + "node_modules/util": { "version": "0.12.5", - "requires": { + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "dependencies": { "inherits": "^2.0.3", "is-arguments": "^1.0.4", "is-generator-function": "^1.0.7", @@ -16090,141 +12499,383 @@ "which-typed-array": "^1.1.2" } }, - "utils-merge": { - "version": "1.0.1" + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } }, - "uuid": { - "version": "8.3.2" + "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" + } }, - "v8-compile-cache-lib": { + "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==", "dev": true }, - "v8-to-istanbul": { - "version": "9.1.0", + "node_modules/v8-to-istanbul": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz", + "integrity": "sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==", "dev": true, - "requires": { + "dependencies": { "@jridgewell/trace-mapping": "^0.3.12", "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0" + "convert-source-map": "^2.0.0" }, - "dependencies": { - "convert-source-map": { - "version": "1.9.0", - "dev": true - } + "engines": { + "node": ">=10.12.0" } }, - "valid-url": { - "version": "1.0.9" + "node_modules/valid-url": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/valid-url/-/valid-url-1.0.9.tgz", + "integrity": "sha512-QQDsV8OnSf5Uc30CKSwG9lnhMPe6exHtTXLRYX8uMwKENy640pU+2BgBL0LRbDh/eYRahNCS7aewCx0wf3NYVA==" }, - "validator": { - "version": "13.9.0" + "node_modules/validator": { + "version": "13.11.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.11.0.tgz", + "integrity": "sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==", + "engines": { + "node": ">= 0.10" + } }, - "vary": { - "version": "1.1.2" + "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" + } }, - "void-elements": { - "version": "3.1.0" + "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" + } }, - "vscode-languageserver-textdocument": { - "version": "1.0.8", + "node_modules/vscode-languageserver-textdocument": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.11.tgz", + "integrity": "sha512-X+8T3GoiwTVlJbicx/sIAF+yuJAqz8VvwJyoMVhwEMoEKE/fkDmrqUgDMyBECcM2A2frVZIUj5HI/ErRXCfOeA==", "dev": true }, - "vscode-uri": { - "version": "3.0.7", + "node_modules/vscode-uri": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz", + "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==", "dev": true }, - "walk-back": { - "version": "5.1.0" + "node_modules/walk-back": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/walk-back/-/walk-back-5.1.0.tgz", + "integrity": "sha512-Uhxps5yZcVNbLEAnb+xaEEMdgTXl9qAQDzKYejG2AZ7qPwRQ81lozY9ECDbjLPNWm7YsO1IK5rsP1KoQzXAcGA==", + "engines": { + "node": ">=12.17" + } }, - "walker": { + "node_modules/walker": { "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", "dev": true, - "requires": { + "dependencies": { "makeerror": "1.0.12" } }, - "which": { + "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/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, - "requires": { + "dependencies": { "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" } }, - "which-typed-array": { - "version": "1.1.9", - "requires": { + "node_modules/which-typed-array": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz", + "integrity": "sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==", + "dependencies": { "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", + "call-bind": "^1.0.4", "for-each": "^0.3.3", "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.10" + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "with": { + "node_modules/with": { "version": "7.0.2", - "requires": { + "resolved": "https://registry.npmjs.org/with/-/with-7.0.2.tgz", + "integrity": "sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==", + "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" } }, - "word-wrap": { - "version": "1.2.3", - "dev": true + "node_modules/wrap-ansi": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", + "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } }, - "wrap-ansi": { + "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, - "requires": { + "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" } }, - "wrappy": { - "version": "1.0.2" + "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==", + "dev": true, + "engines": { + "node": ">=8" + } }, - "write-file-atomic": { - "version": "4.0.2", + "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==", "dev": true, - "requires": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "xdg-basedir": { - "version": "4.0.0", + "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==", + "dev": true, + "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==", + "dev": true + }, + "node_modules/wrap-ansi-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 }, - "xml": { + "node_modules/wrap-ansi-cjs/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/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==", + "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/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==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/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/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/write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/xdg-basedir": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-5.1.0.tgz", + "integrity": "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/xml": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz", + "integrity": "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==", "dev": true }, - "xmlbuilder": { - "version": "11.0.1" + "node_modules/xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "engines": { + "node": ">=4.0" + } }, - "xtend": { - "version": "4.0.2" + "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" + } }, - "y18n": { + "node_modules/y18n": { "version": "5.0.8", - "dev": true + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } }, - "yallist": { - "version": "4.0.0" + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, - "yaml": { - "version": "2.2.2", - "dev": true + "node_modules/yaml": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", + "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==", + "dev": true, + "engines": { + "node": ">= 14" + } }, - "yargs": { + "node_modules/yargs": { "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, - "requires": { + "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", @@ -16232,19 +12883,90 @@ "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" } }, - "yargs-parser": { + "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==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/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/yargs/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 }, - "yn": { + "node_modules/yargs/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/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==", + "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/yargs/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/yn": { "version": "3.1.1", - "dev": true + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } }, - "yocto-queue": { + "node_modules/yocto-queue": { "version": "0.1.0", - "dev": true + "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" + } } } } diff --git a/package.json b/package.json index a86483193..1b2d5c589 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,6 @@ { "name": "opensource-management-portal", "author": "Microsoft Corporation", - "contributors": [ - "Jeff Wilcox " - ], "version": "7.1.0", "license": "MIT", "private": true, @@ -20,20 +17,23 @@ ], "scripts": { "build": "tsc", + "dev:local": "DEBUG=startup EXIT_IMMEDIATELY=1 node ./dist/scripts/localEnvironment.js", "find-deadcode": "ts-prune", + "find-circular-dependencies": "npx madge --circular --extensions ts .", "fix:js": "eslint --fix .", - "fix:md": "markdownlint-cli2-fix \"**/*.md\"", + "fix:md": "markdownlint-cli2 --config ./.config/.markdownlint-cli2.jsonc --fix \"**/*.md\"", "fix": "npm run fix:js && npm run fix:md", "lint:js": "eslint .", - "lint:md": "markdownlint-cli2 \"**/*.md\"", - "lint:spell": "cspell --no-progress \"**\"", - "lint": "npm run lint:js && npm run lint:md && npm run lint:spell", - "prepare": "npx husky install", + "lint:json": "echo OK", + "lint:md": "markdownlint-cli2 --config ./.config/.markdownlint-cli2.jsonc \"**/*.md\"", + "lint:spell": "cspell --config ./.cspell.json --no-progress \"**\"", + "lint": "npm run lint:json && npm run lint:js && npm run lint:md && npm run lint:spell", + "prepare": "husky install || echo No Husky.", "start-4000": "PORT=4000 DEBUG=startup node ./dist/bin/www", "start-in-container": "node ./bin/www", "start": "node ./dist/bin/www", - "test:ci": "jest --watchAll=false --reporters=default --reporters=jest-junit", - "test": "jest" + "test:ci": "jest --config ./test/jest.config.ts --watchAll=false --reporters=default --reporters=jest-junit", + "test": "jest --config ./test/jest.config.ts" }, "lint-staged": { "*": [ @@ -41,7 +41,7 @@ "cspell --no-must-find-files" ], "*.{js,ts}": "eslint --fix", - "*.md": "markdownlint-cli2-fix" + "*.md": "markdownlint-cli2 --fix --config ./.config/.markdownlint-cli2.jsonc" }, "jest-junit": { "suiteNameTemplate": "{filepath}", @@ -65,21 +65,22 @@ "_end_microsoft_internal_": 0, "//...": "- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ", "dependencies": { - "@azure/cosmos": "3.17.3", + "@azure/cosmos": "4.0.0", "@azure/data-tables": "13.2.2", - "@azure/identity": "3.2.2", + "@azure/identity": "4.0.0", "@azure/keyvault-secrets": "4.7.0", - "@azure/service-bus": "7.9.0", - "@azure/storage-blob": "12.14.0", - "@azure/storage-queue": "12.14.0", - "@octokit/plugin-paginate-graphql": "2.0.1", - "@octokit/request": "6.2.5", - "@octokit/auth-app": "4.0.13", - "@octokit/rest": "19.0.11", + "@azure/service-bus": "7.9.3", + "@azure/storage-blob": "12.17.0", + "@azure/storage-queue": "12.16.0", + "@octokit/plugin-paginate-graphql": "4.0.0", + "@octokit/request": "8.1.6", + "@octokit/auth-app": "6.0.3", + "@octokit/rest": "20.0.2", + "@primer/octicons": "19.8.0", "app-root-path": "3.1.0", - "applicationinsights": "2.7.0", + "applicationinsights": "2.9.2", "async-prompt": "1.0.1", - "axios": "1.4.0", + "axios": "1.6.5", "basic-auth": "2.0.1", "body-parser": "1.20.2", "color-contrast-checker": "2.1.0", @@ -90,7 +91,7 @@ "deepmerge": "4.3.1", "dotenv": "16.3.1", "express": "4.18.2", - "express-async-handler": "1.1.4", + "express-async-handler": "1.2.0", "express-session": "1.17.3", "express-sslify": "1.2.0", "file-size": "1.0.0", @@ -99,76 +100,76 @@ "js-yaml": "4.1.0", "json-2-csv": "^3.18.0", "jsonc": "2.0.0", - "jsonwebtoken": "9.0.1", - "jwks-rsa": "3.0.1", + "jsonwebtoken": "9.0.2", + "jwks-rsa": "3.1.0", "language-map": "1.5.0", "lodash": "4.17.21", - "luxon": "3.3.0", + "luxon": "3.4.4", "memory-cache": "0.2.0", - "moment": "2.29.4", + "moment": "2.30.1", "morgan": "1.10.0", "node-jose": "2.2.0", - "nodemailer": "6.9.4", + "nodemailer": "6.9.8", "object-path": "0.11.8", - "octicons": "5.0.1", - "passport": "0.6.0", + "passport": "0.7.0", "passport-azure-ad": "4.3.5", "passport-github": "1.1.0", - "pg": "8.11.0", + "pg": "8.11.3", "pg-escape": "0.2.0", "pug": "3.0.2", "pug-load": "3.0.0", "recursive-readdir": "2.2.3", - "redis": "4.6.8", + "redis": "4.6.12", "secure-compare": "3.0.1", - "semver": "7.5.1", + "semver": "7.5.4", "serve-favicon": "2.5.0", "simple-oauth2": "5.0.0", "throat": "6.0.2", "tmp-promise": "3.0.3", - "validator": "13.9.0", + "validator": "13.11.0", "walk-back": "5.1.0" }, "devDependencies": { - "@types/debug": "4.1.7", - "@types/express": "4.17.17", - "@types/express-session": "1.17.7", - "@types/jest": "29.5.1", - "@types/lodash": "4.14.194", - "@types/luxon": "3.3.0", - "@types/memory-cache": "0.2.2", - "@types/morgan": "1.9.4", - "@types/node": "20.4.2", - "@types/node-jose": "1.1.10", - "@types/object-path": "0.11.1", - "@types/passport": "1.0.12", - "@types/passport-azure-ad": "4.3.1", - "@types/passport-github": "1.1.7", - "@types/pg": "8.10.1", - "@types/pug": "2.0.6", - "@types/recursive-readdir": "2.2.1", - "@types/semver": "7.5.0", - "@types/simple-oauth2": "5.0.4", - "@types/validator": "13.7.17", - "@typescript-eslint/eslint-plugin": "5.59.7", - "@typescript-eslint/parser": "5.59.7", - "cspell": "6.31.1", - "eslint": "8.41.0", - "eslint-config-prettier": "8.8.0", - "eslint-plugin-n": "16.0.0", - "eslint-plugin-prettier": "4.2.1", + "@types/cors": "2.8.17", + "@types/debug": "4.1.12", + "@types/express": "4.17.21", + "@types/express-session": "1.17.10", + "@types/jest": "29.5.11", + "@types/lodash": "4.14.202", + "@types/luxon": "3.4.0", + "@types/memory-cache": "0.2.5", + "@types/morgan": "1.9.9", + "@types/node": "20.11.0", + "@types/node-jose": "1.1.13", + "@types/object-path": "0.11.4", + "@types/passport": "1.0.16", + "@types/passport-azure-ad": "4.3.5", + "@types/passport-github": "1.1.12", + "@types/pg": "8.10.9", + "@types/pug": "2.0.10", + "@types/recursive-readdir": "2.2.4", + "@types/semver": "7.5.6", + "@types/simple-oauth2": "5.0.7", + "@types/validator": "13.11.8", + "@typescript-eslint/eslint-plugin": "6.18.1", + "@typescript-eslint/parser": "6.18.1", + "cspell": "8.3.2", + "eslint": "8.56.0", + "eslint-config-prettier": "9.1.0", + "eslint-plugin-n": "16.6.2", + "eslint-plugin-prettier": "5.1.3", "husky": "8.0.3", - "jest": "29.5.0", + "jest": "29.7.0", "jest-junit": "16.0.0", - "lint-staged": "13.2.2", - "markdownlint-cli2": "0.7.1", - "prettier": "2.8.8", - "ts-jest": "29.1.0", - "ts-node": "10.9.1", + "lint-staged": "15.2.0", + "markdownlint-cli2": "0.12.0", + "prettier": "3.1.1", + "ts-jest": "29.1.1", + "ts-node": "10.9.2", "ts-prune": "0.10.3", - "typescript": "5.0.4" + "typescript": "5.3.3" }, "engines": { - "node": ">=16" + "node": ">=18" } } diff --git a/routes/administration/app.ts b/routes/administration/app.ts index ea3419bb3..b5add5da4 100644 --- a/routes/administration/app.ts +++ b/routes/administration/app.ts @@ -3,19 +3,19 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; const router: Router = Router(); -import { getProviders } from '../../transitional'; +import { getProviders } from '../../lib/transitional'; import { OrganizationSetting, IBasicGitHubAppInstallation, - SpecialTeam, -} from '../../entities/organizationSettings/organizationSetting'; + SystemTeam, +} from '../../business/entities/organizationSettings/organizationSetting'; import { IndividualContext } from '../../business/user'; import { Operations, Organization } from '../../business'; -import GitHubApplication from '../../business/application'; +import GitHubApplication, { isInstallationConfigured } from '../../business/application'; import { ReposAppRequest, IGitHubAppInstallation, @@ -27,7 +27,7 @@ import { router.use( '/:appId', - asyncHandler(async function (req: ReposAppRequest, res, next) { + asyncHandler(async function (req: ReposAppRequest, res: Response, next: NextFunction) { const providers = getProviders(req); const appId = Number(req.params.appId); const app = providers.operations.getApplicationById(appId); @@ -43,7 +43,7 @@ router.use( router.get( '/:appId', - asyncHandler(async function (req: ReposAppRequest, res, next) { + asyncHandler(async function (req: ReposAppRequest, res: Response, next: NextFunction) { const githubApplication = req['githubApplication'] as GitHubApplication; const installationIdString = req.query.installation_id; const setupAction = req.query.setup_action; @@ -71,7 +71,7 @@ router.get( router.use( '/:appId/installations/:installationId', - asyncHandler(async function (req: ReposAppRequest, res, next) { + asyncHandler(async function (req: ReposAppRequest, res: Response, next: NextFunction) { const githubApplication = req['githubApplication'] as GitHubApplication; const installationIdString = req.params.installationId; const { operations, organizationSettingsProvider } = getProviders(req); @@ -101,21 +101,6 @@ router.use( }) ); -function isInstallationConfigured( - settings: OrganizationSetting, - installation: IGitHubAppInstallation -): boolean { - if (!settings || !settings.installations) { - return false; - } - for (const install of settings.installations) { - if (install.installationId === installation.id) { - return true; - } - } - return false; -} - async function getDynamicSettingsFromLegacySettings( operations: Operations, staticSettings: any, @@ -166,7 +151,7 @@ async function getDynamicSettingsFromLegacySettings( router.post( '/:appId/installations/:installationId', - asyncHandler(async function (req: ReposAppRequest, res, next) { + asyncHandler(async function (req: ReposAppRequest, res: Response, next: NextFunction) { const hasBurnButtonClicked = req.body['burn-org-app']; const hasImportButtonClicked = req.body['adopt-import-settings']; const hasCreateButtonClicked = req.body['adopt-new-org']; @@ -229,9 +214,9 @@ router.post( if (result?.state === OrganizationMembershipState.Pending) { return res.send( `You need to accept the membership now at: https://github.com/${unconfiguredOrganization.name}` - ); + ) as unknown as void; } else { - return res.send('OK. Elevation should be all set.'); + return res.send('OK. Elevation should be all set.') as unknown as void; } } catch (error) { return next(error); @@ -311,22 +296,25 @@ router.post( // explicitly now allowing globalSudo to be set here throw new Error(`Unsupported team type: ${teamType}`); } - let specialTeamType: SpecialTeam = null; + let specialTeamType: SystemTeam = null; switch (teamType) { case 'everyone': - specialTeamType = SpecialTeam.Everyone; + specialTeamType = SystemTeam.Everyone; + break; + case 'openAccess': + specialTeamType = SystemTeam.OpenAccess; break; case 'systemAdmin': - specialTeamType = SpecialTeam.SystemAdmin; + specialTeamType = SystemTeam.SystemAdmin; break; case 'systemWrite': - specialTeamType = SpecialTeam.SystemWrite; + specialTeamType = SystemTeam.SystemWrite; break; case 'systemRead': - specialTeamType = SpecialTeam.SystemRead; + specialTeamType = SystemTeam.SystemRead; break; case 'sudo': - specialTeamType = SpecialTeam.Sudo; + specialTeamType = SystemTeam.Sudo; break; // case 'globalSudo': // specialTeamType = SpecialTeam.GlobalSudo; @@ -375,7 +363,7 @@ router.post( router.get( '/:appId/installations/:installationId', - asyncHandler(async function (req: ReposAppRequest, res, next) { + asyncHandler(async function (req: ReposAppRequest, res: Response, next: NextFunction) { const githubApplication = req['githubApplication'] as GitHubApplication; const providers = getProviders(req); const individualContext = req.individualContext; diff --git a/routes/administration/apps.ts b/routes/administration/apps.ts index bec5cb2bc..8221d430d 100644 --- a/routes/administration/apps.ts +++ b/routes/administration/apps.ts @@ -3,39 +3,22 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; const router: Router = Router(); -import { getProviders } from '../../transitional'; -import { sortByCaseInsensitive } from '../../utils'; +import { getProviders } from '../../lib/transitional'; +import { sortByCaseInsensitive } from '../../lib/utils'; import GitHubApplication from '../../business/application'; -import { OrganizationSetting } from '../../entities/organizationSettings/organizationSetting'; import { ReposAppRequest, UserAlertType } from '../../interfaces'; - -interface IByOrgViewAppInstallation { - app: GitHubApplication; - installationId?: number; -} - -enum OrgStatus { - Active = 'Active', - Adopted = 'Adopted', - NotAdopted = 'NotAdopted', -} - -interface IByOrgView { - organizationName: string; - status: OrgStatus; - appInstallations: Map; - dynamicSettings: OrganizationSetting; - configuredInstallations: number[]; - id?: number; -} +import { + ManagedOrganizationAppConfigurationsByOrgView, + ManagedOrganizationStatus, +} from '../../api/client/context/administration/types'; router.post( '/', - asyncHandler(async function (req: ReposAppRequest, res, next) { + asyncHandler(async function (req: ReposAppRequest, res: Response, next: NextFunction) { const providers = getProviders(req); const { deletesettingsorgname } = req.body; if (!deletesettingsorgname) { @@ -69,20 +52,20 @@ router.post( router.get( '/', - asyncHandler(async function (req: ReposAppRequest, res, next) { + asyncHandler(async function (req: ReposAppRequest, res: Response, next: NextFunction) { const providers = getProviders(req); const operations = providers.operations; const apps = providers.operations.getApplications(); const individualContext = req.individualContext; const organizationSettingsProvider = providers.organizationSettingsProvider; - const byOrg = new Map(); + const byOrg = new Map(); function getOrg(name: string) { let o = byOrg.get(name); if (!o) { o = { organizationName: name, id: undefined, - status: OrgStatus.NotAdopted, + status: ManagedOrganizationStatus.NotAdopted, appInstallations: new Map(), dynamicSettings: null, configuredInstallations: [], @@ -96,19 +79,19 @@ router.get( } for (const app of apps) { const appInstalls = await app.getInstallations({ maxAgeSeconds: 5 }); - const { valid } = GitHubApplication.filterInstallations(appInstalls); - for (const vi of valid) { - const organizationName = vi.account.login; + const { valid: validInstallations } = GitHubApplication.filterInstallations(appInstalls); + for (const valid of validInstallations) { + const organizationName = valid.account.login; const o = getOrg(organizationName.toLowerCase()); o.appInstallations.set(app.id, { app, - installationId: vi.id, + installationId: valid.id, }); - o.id = Number(vi.target_id); - if (!o.dynamicSettings && vi.target_type === 'Organization') { + o.id = Number(valid.target_id); + if (!o.dynamicSettings && valid.target_type === 'Organization') { try { o.dynamicSettings = await organizationSettingsProvider.getOrganizationSetting( - vi.target_id.toString() + valid.target_id.toString() ); } catch (ignore) { /* ignored */ @@ -117,10 +100,10 @@ router.get( o.configuredInstallations = o.dynamicSettings.installations.map( (install) => install.installationId ); - o.status = OrgStatus.Adopted; + o.status = ManagedOrganizationStatus.Adopted; } if (o.dynamicSettings && o.dynamicSettings.active === true) { - o.status = OrgStatus.Active; + o.status = ManagedOrganizationStatus.Active; } } } diff --git a/routes/administration/index.ts b/routes/administration/index.ts index 7fcc6c437..605915110 100644 --- a/routes/administration/index.ts +++ b/routes/administration/index.ts @@ -3,12 +3,12 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; const router: Router = Router(); import { ReposAppRequest } from '../../interfaces'; -import { getProviders } from '../../transitional'; +import { getProviders } from '../../lib/transitional'; import getCompanySpecificDeployment from '../../middleware/companySpecificDeployment'; @@ -20,7 +20,7 @@ import _ from 'lodash'; router.use( '*', - asyncHandler(async function (req: ReposAppRequest, res, next) { + asyncHandler(async function (req: ReposAppRequest, res: Response, next: NextFunction) { const { corporateAdministrationProfile } = getProviders(req); if (corporateAdministrationProfile && corporateAdministrationProfile.urls) { req.individualContext.setInitialViewProperty('_corpAdminUrls', corporateAdministrationProfile.urls); @@ -41,7 +41,7 @@ try { router.use('/app', RouteApp); router.use('/apps', RouteApps); -router.get('/', (req: ReposAppRequest, res, next) => { +router.get('/', (req: ReposAppRequest, res: Response, next: NextFunction) => { const individualContext = req.individualContext; individualContext.webContext.render({ view: 'administration', diff --git a/routes/approvals.ts b/routes/approvals.ts index d00d69b50..15ea37dcf 100644 --- a/routes/approvals.ts +++ b/routes/approvals.ts @@ -3,7 +3,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; const router: Router = Router(); router.get('/', (req, res) => { diff --git a/routes/diagnostics.ts b/routes/diagnostics.ts index 9ae9392aa..b3e09342b 100644 --- a/routes/diagnostics.ts +++ b/routes/diagnostics.ts @@ -4,10 +4,11 @@ // import { Request, Router } from 'express'; +import asyncHandler from 'express-async-handler'; const router: Router = Router(); import { IAppSession, ReposAppRequest } from '../interfaces'; -import { getProviders } from '../transitional'; +import { CreateError, getProviders } from '../lib/transitional'; const redacted = '*****'; @@ -96,4 +97,15 @@ router.get('/', (req: IRequestWithSession, res) => { }); }); +router.get( + '/advanced', + asyncHandler(async (req: ReposAppRequest, res, next) => { + if (req.user?.azure?.oid !== 'b9f9877e-1cae-445e-bc28-3c943078c8e7') { + return next(CreateError.NotAuthorized('You are not authorized to view this page.')); + } + const obj: any = Object.assign({}, process.env); + return res.json(obj) as unknown as void; + }) +); + export default router; diff --git a/routes/explore.ts b/routes/explore.ts index b912b71cb..efeee696f 100644 --- a/routes/explore.ts +++ b/routes/explore.ts @@ -3,11 +3,11 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; const router: Router = Router(); import { ReposAppRequest } from '../interfaces'; -import { getProviders } from '../transitional'; +import { getProviders } from '../lib/transitional'; router.get('/', (req: ReposAppRequest, res) => { const config = getProviders(req).config; diff --git a/routes/index-authenticated.ts b/routes/index-authenticated.ts index 688be52f5..a5ee54b79 100644 --- a/routes/index-authenticated.ts +++ b/routes/index-authenticated.ts @@ -5,11 +5,11 @@ import _ from 'lodash'; -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; const router: Router = Router(); -import { hasStaticReactClientApp, getProviders } from '../transitional'; +import { hasStaticReactClientApp, getProviders } from '../lib/transitional'; import { Organization } from '../business/organization'; @@ -27,15 +27,17 @@ import linkRoute from './link'; import linkedUserRoute from './index-linked'; import linkCleanupRoute from './link-cleanup'; -import SettingsRoute from './settings'; +import routeSettings from './settings'; import getCompanySpecificDeployment from '../middleware/companySpecificDeployment'; const hasReactApp = hasStaticReactClientApp(); const reactRoute = hasReactApp ? injectReactClient() : undefined; -import RoutePlaceholders from './placeholders'; -import RouteReleasesSpa from './releasesSpa'; +import routePlaceholders from './placeholders'; +import routeReleasesSpa from './releasesSpa'; + import { ReposAppRequest, UserAlertType } from '../interfaces'; +import { Repository } from '../business'; // - - - Middleware: require that they have a passport - - - router.use(asyncHandler(requireAuthenticatedUserOrSignIn)); @@ -48,10 +50,10 @@ router.use(asyncHandler(AddLinkToRequest)); router.use(asyncHandler(blockEnterpriseManagedUsersAuthentication)); -router.use('/placeholder', RoutePlaceholders); +router.use('/placeholder', routePlaceholders); router.use('/link/cleanup', reactRoute || linkCleanupRoute); router.use('/link', reactRoute || linkRoute); -router.use('/releases', reactRoute || RouteReleasesSpa); +router.use('/releases', reactRoute || routeReleasesSpa); if (reactRoute) { // client-only routes @@ -67,9 +69,9 @@ const dynamicStartupInstance = getCompanySpecificDeployment(); dynamicStartupInstance?.routes?.connectAuthenticatedRoutes && dynamicStartupInstance.routes.connectAuthenticatedRoutes(router, reactRoute); -router.use('/settings', SettingsRoute); +router.use('/settings', reactRoute || routeSettings); -router.get('/news', (req: ReposAppRequest, res, next) => { +router.get('/news', (req: ReposAppRequest, res: Response, next: NextFunction) => { const config = getProviders(req).config; if (config && config.news && config.news.all && config.news.all.length) { return req.individualContext.webContext.render({ @@ -81,6 +83,40 @@ router.get('/news', (req: ReposAppRequest, res, next) => { } }); +router.use( + '/', + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { + // Helper method to allow pasting a GitHub URL into the app to go to a repo + const { rid, oid, action } = req.query; + const { operations } = getProviders(req); + if (!rid && !oid) { + return next(); + } + const repositoryId = Number(rid); + const organizationId = Number(oid); + let organization: Organization = null; + let repository: Repository = null; + try { + organization = operations.getOrganizationById(organizationId); + } catch (error) { + // no-op continue + return next(); + } + if (organization) { + try { + repository = await organization.getRepositoryById(repositoryId); + return res.redirect( + `/orgs/${organization.name}/repos/${repository.name}${action ? `/${action}` : ''}` + ); + } catch (error) { + // no-op continue + return next(); + } + } + return next(); + }) +); + // Link cleanups and check their signed-in username vs their link router.use(RequireLinkMatchesGitHubSessionExceptPrefixedRoute('/unlink')); @@ -88,7 +124,7 @@ router.use(RequireLinkMatchesGitHubSessionExceptPrefixedRoute('/unlink')); router.get( '/', reactRoute || - asyncHandler(async function (req: ReposAppRequest, res, next) { + asyncHandler(async function (req: ReposAppRequest, res: Response, next: NextFunction) { const onboarding = req.query.onboarding !== undefined; const individualContext = req.individualContext; const link = individualContext.link; diff --git a/routes/index-linked.ts b/routes/index-linked.ts index fef788ced..5c064e058 100644 --- a/routes/index-linked.ts +++ b/routes/index-linked.ts @@ -3,13 +3,13 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; const router: Router = Router(); -import { CreateError, hasStaticReactClientApp, getProviders } from '../transitional'; +import { CreateError, hasStaticReactClientApp, getProviders } from '../lib/transitional'; import { IndividualContext } from '../business/user'; -import { storeOriginalUrlAsVariable } from '../utils'; +import { storeOriginalUrlAsVariable } from '../lib/utils'; import { AuthorizeOnlyCorporateAdministrators } from '../middleware/business/corporateAdministrators'; import RouteAdministration from './administration'; @@ -38,7 +38,7 @@ const reactRoute = hasReactApp ? injectReactClient() : undefined; // * only for the traditional app. The React app does not require a link to browse orgs. //----------------------------------------------------------------------------- if (!hasReactApp) { - router.use(function (req: ReposAppRequest, res, next) { + router.use(function (req: ReposAppRequest, res: Response, next: NextFunction) { const individualContext = req.individualContext as IndividualContext; const link = individualContext.link; if (link && link.thirdPartyId) { @@ -65,7 +65,7 @@ router.use('/administration', asyncHandler(AuthorizeOnlyCorporateAdministrators) router.use( '/https?*github.com/:org/:repo', - asyncHandler(async (req: ReposAppRequest, res, next) => { + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { // Helper method to allow pasting a GitHub URL into the app to go to a repo const { org, repo } = req.params; const { operations } = getProviders(req); diff --git a/routes/index.ts b/routes/index.ts index 84d7d2fd0..16cc579aa 100644 --- a/routes/index.ts +++ b/routes/index.ts @@ -3,7 +3,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; const router: Router = Router(); import { webContextMiddleware } from '../middleware/business/setContext'; @@ -16,7 +16,7 @@ import ExploreRoute from './explore'; import ApprovalsRoute from './approvals'; import AuthenticatedRoute from './index-authenticated'; -import { hasStaticReactClientApp } from '../transitional'; +import { hasStaticReactClientApp } from '../lib/transitional'; import { injectReactClient } from '../middleware'; router.use('/api/client', clientApiRoute); diff --git a/routes/link-cleanup.ts b/routes/link-cleanup.ts index ff7156a32..d8ba92b7e 100644 --- a/routes/link-cleanup.ts +++ b/routes/link-cleanup.ts @@ -3,7 +3,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; const router: Router = Router(); import { ReposAppRequest } from '../interfaces'; @@ -17,7 +17,7 @@ import { ReposAppRequest } from '../interfaces'; // linksForCleanup?: any; // } -router.use((req: ReposAppRequest, res, next) => { +router.use((req: ReposAppRequest, res: Response, next: NextFunction) => { // TODO: revisit implementation return next(); @@ -104,7 +104,7 @@ router.use((req: ReposAppRequest, res, next) => { // renderCleanupPage(req, res, null, null); // }); -// router.post('/', (req: IRequestWithSessionPlus, res, next) => { +// router.post('/', (req: IRequestWithSessionPlus, res: Response, next: NextFunction) => { // let action = 'unlink'; // let id = req.body.unlink; // if (!req.body.unlink && req.session && req.session.enableMultipleAccounts === true && req.body.select) { diff --git a/routes/link.ts b/routes/link.ts index 312325395..eab59a277 100644 --- a/routes/link.ts +++ b/routes/link.ts @@ -3,7 +3,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; const router: Router = Router(); @@ -14,9 +14,9 @@ import { ICorporateLink, LinkOperationSource, } from '../interfaces'; -import { getProviders, splitSemiColonCommas } from '../transitional'; +import { getProviders, splitSemiColonCommas } from '../lib/transitional'; import { IndividualContext } from '../business/user'; -import { isCodespacesAuthenticating, storeOriginalUrlAsReferrer, wrapError } from '../utils'; +import { isCodespacesAuthenticating, storeOriginalUrlAsReferrer, wrapError } from '../lib/utils'; import validator from 'validator'; @@ -31,7 +31,7 @@ interface IRequestHacked extends ReposAppRequest { overrideLinkUserPrincipalName?: any; } -router.use((req: IRequestHacked, res, next) => { +router.use((req: IRequestHacked, res: Response, next: NextFunction) => { const config = getProviders(req).config; if ( config && @@ -50,7 +50,7 @@ router.use((req: IRequestHacked, res, next) => { router.use( '/', - asyncHandler(async function (req: ReposAppRequest, res, next) { + asyncHandler(async function (req: ReposAppRequest, res: Response, next: NextFunction) { // Make sure both account types are authenticated before showing the link pg [wi 12690] const individualContext = req.individualContext; if (!individualContext.corporateIdentity || !individualContext.getGitHubIdentity()) { @@ -64,7 +64,7 @@ router.use( // TODO: graph provider non-guest check should be middleware and in the link business process router.use( - asyncHandler(async (req: IRequestHacked, res, next) => { + asyncHandler(async (req: IRequestHacked, res: Response, next: NextFunction) => { const individualContext = req.individualContext as IndividualContext; const providers = getProviders(req); const insights = providers.insights; @@ -155,7 +155,7 @@ router.use( router.get( '/', - asyncHandler(async function (req: ReposAppRequest, res, next) { + asyncHandler(async function (req: ReposAppRequest, res: Response, next: NextFunction) { const { config } = getProviders(req); const individualContext = req.individualContext; const link = individualContext.link; @@ -233,7 +233,7 @@ router.get('/enableMultipleAccounts', function (req: IRequestWithSession, res) { router.post( '/', - asyncHandler(async (req: ReposAppRequest, res, next) => { + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { const individualContext = req.individualContext as IndividualContext; try { await interactiveLinkUser(false, individualContext, req, res, next); @@ -291,7 +291,7 @@ export async function interactiveLinkUser( router.use('/remove', unlinkRoute); -router.get('/reconnect', function (req: ReposAppRequest, res, next) { +router.get('/reconnect', function (req: ReposAppRequest, res: Response, next: NextFunction) { const config = getProviders(req).config; if (config.authentication.scheme !== 'aad') { return next( diff --git a/routes/org/2fa.ts b/routes/org/2fa.ts index aa1fd3a7a..b4b1b8ac9 100644 --- a/routes/org/2fa.ts +++ b/routes/org/2fa.ts @@ -3,18 +3,18 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; const router: Router = Router(); import moment from 'moment'; import { NoCacheNoBackground, ReposAppRequest } from '../../interfaces'; -import { wrapError } from '../../utils'; +import { wrapError } from '../../lib/utils'; router.get( '/', - asyncHandler(async function (req: ReposAppRequest, res, next) { + asyncHandler(async function (req: ReposAppRequest, res: Response, next: NextFunction) { const organization = req.organization; const onboarding = req.query.onboarding; const joining = req.query.joining; diff --git a/routes/org/index.ts b/routes/org/index.ts index 28281c3b1..d66f45efb 100644 --- a/routes/org/index.ts +++ b/routes/org/index.ts @@ -3,12 +3,12 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import express, { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; const router: Router = Router(); -import { getProviders } from '../../transitional'; +import { getProviders } from '../../lib/transitional'; import { IAggregateUserSummary } from '../../business/user/aggregate'; -import { TeamJoinApprovalEntity } from '../../entities/teamJoinApproval/teamJoinApproval'; +import { TeamJoinApprovalEntity } from '../../business/entities/teamJoinApproval/teamJoinApproval'; import { Team } from '../../business'; import { ReposAppRequest, OrganizationMembershipState } from '../../interfaces'; import { @@ -31,7 +31,7 @@ interface ILocalOrgRequest extends ReposAppRequest { orgPermissions?: IRequestOrganizationPermissions; } -router.use(function (req: ReposAppRequest, res, next) { +router.use(function (req: ReposAppRequest, res: Response, next: NextFunction) { const onboarding = req.query.onboarding; const organization = req.organization; req.individualContext.webContext.pushBreadcrumb(organization.name, onboarding ? false : undefined); @@ -43,7 +43,7 @@ router.use(function (req: ReposAppRequest, res, next) { }); // Campaign-related redirect to take the user to GitHub -router.get('/', (req: ReposAppRequest, res, next) => { +router.get('/', (req: ReposAppRequest, res: Response, next: NextFunction) => { const providers = getProviders(req); if (!providers || !providers.campaign) { return next(); @@ -63,7 +63,7 @@ router.use('/teams', RouteTeams); router.use(asyncHandler(AddOrganizationPermissionsToRequest)); router.use( - asyncHandler(async (req: ILocalOrgRequest, res, next) => { + asyncHandler(async (req: ILocalOrgRequest, res: Response, next: NextFunction) => { const organization = req.organization; const orgPermissions = req.orgPermissions; if (!orgPermissions) { @@ -90,7 +90,7 @@ router.use( router.get( '/', - asyncHandler(async function (req: ReposAppRequest, res, next) { + asyncHandler(async function (req: ReposAppRequest, res: Response, next: NextFunction) { const providers = getProviders(req); const approvalProvider = providers.approvalProvider; const organization = req.organization; @@ -107,9 +107,8 @@ router.get( pendingApprovals: null as TeamJoinApprovalEntity[], organizationAdmins, }; - results.organizationOverview = await individualContext.aggregations.getAggregatedOrganizationOverview( - organization - ); + results.organizationOverview = + await individualContext.aggregations.getAggregatedOrganizationOverview(organization); // Check for pending approvals const teamsMaintained = results.organizationOverview.teams.maintainer as Team[]; if (teamsMaintained && teamsMaintained.length && teamsMaintained.length > 0) { @@ -147,7 +146,7 @@ router.use('/wizard', RouteNewRepoSpa); router.use( '/:repoName', - asyncHandler(async (req: ReposAppRequest, res, next) => { + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { const repoName = req.params.repoName; const organization = req.organization; const attemptedRepository = organization.repository(repoName); diff --git a/routes/org/join.ts b/routes/org/join.ts index 30c4435d8..c136320ea 100644 --- a/routes/org/join.ts +++ b/routes/org/join.ts @@ -10,9 +10,10 @@ import asyncHandler from 'express-async-handler'; const router: Router = Router(); import querystring from 'querystring'; -import { CreateError, getProviders } from '../../transitional'; + +import { CreateError, getProviders } from '../../lib/transitional'; import { IndividualContext } from '../../business/user'; -import { sleep, storeOriginalUrlAsReferrer, wrapError } from '../../utils'; +import { sleep, storeOriginalUrlAsReferrer, wrapError } from '../../lib/utils'; import RequireActiveGitHubSession from '../../middleware/github/requireActiveSession'; import { jsonError } from '../../middleware/jsonError'; import { Organization, Team } from '../../business'; @@ -31,8 +32,8 @@ import getCompanySpecificDeployment from '../../middleware/companySpecificDeploy //------------- // Join checks //------------- -router.use(function (req: ReposAppRequest, res, next) { - const { organization } = req; +router.use(function (req: ReposAppRequest, res: Response, next: NextFunction) { + const organization = req.organization; let err = null; if (organization.locked) { diff --git a/routes/org/leave.ts b/routes/org/leave.ts index 2ee5b6cff..60b3b654f 100644 --- a/routes/org/leave.ts +++ b/routes/org/leave.ts @@ -3,12 +3,12 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; const router: Router = Router(); -import { getProviders } from '../../transitional'; -import { wrapError } from '../../utils'; +import { getProviders } from '../../lib/transitional'; +import { wrapError } from '../../lib/utils'; import { Organization } from '../../business'; import { OrganizationMembershipState, ReposAppRequest, UserAlertType } from '../../interfaces'; @@ -25,7 +25,7 @@ interface ILocalLeaveRequest extends ReposAppRequest { } router.use( - asyncHandler(async (req: ILocalLeaveRequest, res, next) => { + asyncHandler(async (req: ILocalLeaveRequest, res: Response, next: NextFunction) => { const organization = req.organization as Organization; req.orgLeave = { state: null, @@ -58,7 +58,7 @@ router.get('/', function (req: ILocalLeaveRequest, res) { router.post( '/', - asyncHandler(async function (req: ReposAppRequest, res, next) { + asyncHandler(async function (req: ReposAppRequest, res: Response, next: NextFunction) { const organization = req.organization; const providers = getProviders(req); const operations = providers.operations; diff --git a/routes/org/membership.ts b/routes/org/membership.ts index 1e5614ce6..1ab72ab62 100644 --- a/routes/org/membership.ts +++ b/routes/org/membership.ts @@ -3,18 +3,18 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; import { ReposAppRequest, UserAlertType } from '../../interfaces'; -import { wrapError } from '../../utils'; +import { wrapError } from '../../lib/utils'; import RequireActiveGitHubSession from '../../middleware/github/requireActiveSession'; const router: Router = Router(); router.get( '/', RequireActiveGitHubSession, - asyncHandler(async function (req: ReposAppRequest, res, next) { + asyncHandler(async function (req: ReposAppRequest, res: Response, next: NextFunction) { const organization = req.organization; if (!organization) { // TODO: Was this ever a possible situation? What's going on here? Probably was v1 (single-org) @@ -56,7 +56,7 @@ router.get( router.post( '/', RequireActiveGitHubSession, - asyncHandler(async function (req: ReposAppRequest, res, next) { + asyncHandler(async function (req: ReposAppRequest, res: Response, next: NextFunction) { const username = req.individualContext.getGitHubIdentity().username; const organization = req.organization; if (!organization) { diff --git a/routes/org/newRepoSpa.ts b/routes/org/newRepoSpa.ts index 3adafdcb5..715c45aa5 100644 --- a/routes/org/newRepoSpa.ts +++ b/routes/org/newRepoSpa.ts @@ -3,14 +3,14 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; const router: Router = Router(); import { getRepositoryMetadataProvider, ReposAppRequest } from '../../interfaces'; -import { getProviders } from '../../transitional'; +import { getProviders } from '../../lib/transitional'; import { Organization } from '../../business/organization'; -import NewRepositoryLockdownSystem from '../../features/newRepositories/newRepositoryLockdown'; +import NewRepositoryLockdownSystem from '../../business/features/newRepositories/newRepositoryLockdown'; router.get( '/', diff --git a/routes/org/people.ts b/routes/org/people.ts index 5fefb4fba..ceabce61c 100644 --- a/routes/org/people.ts +++ b/routes/org/people.ts @@ -3,14 +3,14 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; const router: Router = Router(); import { ReposAppRequest } from '../../interfaces'; import RoutePeopleSearch from '../peopleSearch'; -router.use(function (req: ReposAppRequest, res, next) { +router.use(function (req: ReposAppRequest, res: Response, next: NextFunction) { req.individualContext.webContext.pushBreadcrumb('People'); req.reposContext = { section: 'people', diff --git a/routes/org/profileReview.ts b/routes/org/profileReview.ts index 0faf21174..ab65d191f 100644 --- a/routes/org/profileReview.ts +++ b/routes/org/profileReview.ts @@ -3,12 +3,12 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; const router: Router = Router(); import asyncHandler from 'express-async-handler'; import { ReposAppRequest } from '../../interfaces'; -import { getProviders } from '../../transitional'; +import { getProviders } from '../../lib/transitional'; interface IUserProfileWarnings { company?: string; @@ -17,7 +17,7 @@ interface IUserProfileWarnings { router.get( '/', - asyncHandler(async function (req: ReposAppRequest, res, next) { + asyncHandler(async function (req: ReposAppRequest, res: Response, next: NextFunction) { const organization = req.organization; const { operations } = getProviders(req); const config = operations.config; diff --git a/routes/org/repoAdministrativeLock.ts b/routes/org/repoAdministrativeLock.ts index 94323057d..a0dcd5d5e 100644 --- a/routes/org/repoAdministrativeLock.ts +++ b/routes/org/repoAdministrativeLock.ts @@ -4,19 +4,19 @@ // import asyncHandler from 'express-async-handler'; -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; const router: Router = Router(); -import { getProviders } from '../../transitional'; +import { getProviders } from '../../lib/transitional'; import { Repository } from '../../business/repository'; -import { RepositoryMetadataEntity } from '../../entities/repositoryMetadata/repositoryMetadata'; +import { RepositoryMetadataEntity } from '../../business/entities/repositoryMetadata/repositoryMetadata'; import { Organization } from '../../business/organization'; -import NewRepositoryLockdownSystem from '../../features/newRepositories/newRepositoryLockdown'; +import NewRepositoryLockdownSystem from '../../business/features/newRepositories/newRepositoryLockdown'; import { getRepositoryMetadataProvider, ReposAppRequest, UserAlertType } from '../../interfaces'; router.use( '/', - asyncHandler(async (req: ReposAppRequest, res, next) => { + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { const organization = req.organization as Organization; if (!organization.isNewRepositoryLockdownSystemEnabled()) { return next(new Error('This endpoint is not available as configured')); @@ -27,7 +27,7 @@ router.use( router.use( '/', - asyncHandler(async (req: ReposAppRequest, res, next) => { + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { const individualContext = req.individualContext; const isPortalAdministrator = await individualContext.isPortalAdministrator(); if (!isPortalAdministrator) { @@ -42,7 +42,7 @@ router.use( router.get( '/', - asyncHandler(async (req: ReposAppRequest, res, next) => { + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { const repository = req['repository'] as Repository; const repositoryMetadata = req['repositoryMetadata'] as RepositoryMetadataEntity; return renderPage(req, repositoryMetadata, repository); @@ -51,9 +51,9 @@ router.get( router.post( '/', - asyncHandler(async (req: ReposAppRequest, res, next) => { + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { const providers = getProviders(req); - const operations = providers.operations; + const { insights, operations } = providers; const repository = req['repository'] as Repository; const entity = repository.getEntity(); if (!entity.parent) { @@ -68,6 +68,7 @@ router.post( const repositoryMetadataProvider = getRepositoryMetadataProvider(operations); const organization = repository.organization; const lockdownSystem = new NewRepositoryLockdownSystem({ + insights, operations, organization, repository, diff --git a/routes/org/repoWorkflowEngine.ts b/routes/org/repoWorkflowEngine.ts index e1979b26c..4a79b069d 100644 --- a/routes/org/repoWorkflowEngine.ts +++ b/routes/org/repoWorkflowEngine.ts @@ -3,13 +3,14 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +import { NextFunction, Response } from 'express'; import fs from 'fs'; import path from 'path'; import recursiveReadDirectory from 'recursive-readdir'; -import { wrapError, sleep } from '../../utils'; +import { wrapError, sleep } from '../../lib/utils'; import { Organization } from '../../business'; -import { RepositoryMetadataEntity } from '../../entities/repositoryMetadata/repositoryMetadata'; +import { RepositoryMetadataEntity } from '../../business/entities/repositoryMetadata/repositoryMetadata'; import { Repository } from '../../business'; import { CreateRepositoryEntrypoint, ICreateRepositoryApiResult } from '../../api/createRepo'; import { @@ -20,13 +21,14 @@ import { IAlternateTokenOption, IOperationsRepositoryMetadataProvider, IProviders, + IReposAppWithTeam, throwIfNotCapable, } from '../../interfaces'; -import { ErrorHelper } from '../../transitional'; +import { ErrorHelper } from '../../lib/transitional'; import { setupRepositoryReadmeSubstring, setupRepositorySubstring, -} from '../../features/newRepositories/strings'; +} from '../../business/features/newRepositories/strings'; export interface IApprovalPackage { id: string; @@ -83,7 +85,11 @@ export class RepoWorkflowEngine { private log: IRepositoryWorkflowOutput[] = []; private repository: Repository; - constructor(private providers: IProviders, organization: Organization, approvalPackage: IApprovalPackage) { + constructor( + private providers: IProviders, + organization: Organization, + approvalPackage: IApprovalPackage + ) { this.request = approvalPackage.repositoryMetadata; // this.user = approvalPackage.requestingUser; this.id = approvalPackage.id; @@ -185,7 +191,7 @@ export class RepoWorkflowEngine { }); } - editPost(req, res, next) { + editPost(req: IReposAppWithTeam, res: Response, next: NextFunction) { const { operations } = this.providers; const ops = throwIfNotCapable( operations, diff --git a/routes/org/repos.ts b/routes/org/repos.ts index 33b06df44..c3560ffff 100644 --- a/routes/org/repos.ts +++ b/routes/org/repos.ts @@ -3,7 +3,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; const router: Router = Router(); @@ -13,13 +13,13 @@ import moment from 'moment'; import lowercaser from '../../middleware/lowercaser'; import routeAdministrativeLock from './repoAdministrativeLock'; -import NewRepositoryLockdownSystem from '../../features/newRepositories/newRepositoryLockdown'; +import NewRepositoryLockdownSystem from '../../business/features/newRepositories/newRepositoryLockdown'; import { IGraphEntry } from '../../lib/graphProvider'; import { IMail } from '../../lib/mailProvider'; import { IndividualContext } from '../../business/user'; import { Repository, Collaborator, TeamPermission, Organization, OrganizationMember } from '../../business'; -import { RepositoryMetadataEntity } from '../../entities/repositoryMetadata/repositoryMetadata'; +import { RepositoryMetadataEntity } from '../../business/entities/repositoryMetadata/repositoryMetadata'; import { ReposAppRequest, GitHubCollaboratorAffiliationQuery, @@ -35,7 +35,7 @@ import { getContextualRepositoryPermissions, IContextualRepositoryPermissions, } from '../../middleware/github/repoPermissions'; -import { getProviders, CreateError, ErrorHelper } from '../../transitional'; +import { getProviders, CreateError, ErrorHelper } from '../../lib/transitional'; import RouteReposPager from '../reposPager'; @@ -60,7 +60,7 @@ const teamsFilterType = { systemTeamsOnly: 'systemTeamsOnly', }; -router.use(function (req: ReposAppRequest, res, next) { +router.use(function (req: ReposAppRequest, res: Response, next: NextFunction) { req.individualContext.webContext.pushBreadcrumb('Repositories'); req.reposContext = { section: 'repos', @@ -85,10 +85,10 @@ function sliceCollaboratorsForView(collaborators) { const destination = permission.admin ? collabView.administrators : permission.push - ? collabView.writers - : permission.pull - ? collabView.readers - : null; + ? collabView.writers + : permission.pull + ? collabView.readers + : null; if (destination) { destination.push(collab); } @@ -160,7 +160,7 @@ export async function findRepoCollaboratorsExcludingOwners( router.use( '/:repoName', - asyncHandler(async function (req: ILocalRequest, res, next) { + asyncHandler(async function (req: ILocalRequest, res: Response, next: NextFunction) { const repoName = req.params.repoName; const organization = req.organization; const repository = organization.repository(repoName); @@ -175,7 +175,7 @@ router.use('/:repoName/administrativeLock', routeAdministrativeLock); router.use( '/:repoName/delete', - asyncHandler(async function (req: ILocalRequest, res, next) { + asyncHandler(async function (req: ILocalRequest, res: Response, next: NextFunction) { const individualContext = req.individualContext; const repository = req.repository; const organization = req.organization; @@ -205,7 +205,7 @@ router.use( router.get( '/:repoName/delete', - asyncHandler(async function (req: ILocalRequest, res, next) { + asyncHandler(async function (req: ILocalRequest, res: Response, next: NextFunction) { return req.individualContext.webContext.render({ title: 'Delete the repo you created', view: 'repos/delete', @@ -218,13 +218,14 @@ router.get( router.post( '/:repoName/delete', - asyncHandler(async function (req: ILocalRequest, res, next) { + asyncHandler(async function (req: ILocalRequest, res: Response, next: NextFunction) { // NOTE: this code is also duplicated for now in the client/internal/* folder // CONSIDER: de-duplicate - const { operations } = getProviders(req); + const { insights, operations } = getProviders(req); const { organization, repository } = req; const repositoryMetadataProvider = getRepositoryMetadataProvider(operations); const lockdownSystem = new NewRepositoryLockdownSystem({ + insights, operations, organization, repository, @@ -249,121 +250,34 @@ export interface IRenameOutput { } router.post( - '/:repoName/defaultBranch', + '/:repoName', asyncHandler(AddRepositoryPermissionsToRequest), - asyncHandler(async function (req: ILocalRequest, res, next) { - try { - const targetBranchName = req.body.targetBranchName || 'main'; - const providers = getProviders(req); - const activeContext = (req.individualContext || req.apiContext) as IndividualContext; - const repoPermissions = getContextualRepositoryPermissions(req); - const repository = req.repository as Repository; - const outcome = await renameRepositoryDefaultBranchEndToEnd( - providers, - activeContext, - repoPermissions, - repository, - targetBranchName, - false - ); - req.individualContext.webContext.render({ - view: 'repos/repoBranchRenamed', - title: outcome.message, - state: { - output: outcome.output, - repository, - }, - }); - } catch (error) { - return next(error); + asyncHandler(async function (req: ILocalRequest, res: Response, next: NextFunction) { + const repoPermissions = req.repoPermissions; + if (!repoPermissions.allowAdministration) { + return next(new Error('You do not have administrative permission on this repository')); } + // only supporting the 'take public' operation now + const takePublic = req.body['make-repo-public']; + if (!takePublic) { + return next(new Error('Unsupported operation')); + } + const repository = req.repository as Repository; + await repository.editPublicPrivate({ private: false }); + req.individualContext.webContext.saveUserAlert( + `${repository.full_name} is now public.`, + 'Repository publish', + UserAlertType.Success + ); + await repository.getDetails(NoCacheNoBackground); + return res.redirect(`/${repository.organization.name}/repos/${repository.name}?published`); }) ); -export async function renameRepositoryDefaultBranchEndToEnd( - providers: IProviders, - activeContext: IndividualContext, - repoPermissions: IContextualRepositoryPermissions, - repository: Repository, - targetBranchName: string, - waitForRefresh: boolean -): Promise { - const corporateUsername = activeContext.corporateIdentity.username; - if (!corporateUsername) { - throw CreateError.InvalidParameters('no corporate username in the session'); - } - if (!targetBranchName) { - throw CreateError.InvalidParameters('invalid target branch name'); - } - if (!repoPermissions) { - throw CreateError.InvalidParameters('no repo permissions'); - } - if (!repoPermissions.allowAdministration) { - throw CreateError.NotAuthorized('You do not have administrative permission on this repository'); - } - await repository.getDetails(); - function finishUp(): Promise { - return new Promise((resolve) => { - triggerRenameNotification(providers, repository, corporateUsername, targetBranchName, output) - .then((ok) => { - /* ignore */ - }) - .catch((error) => { - console.error(`Notify rename trigger: ${error}`); - }); - repository - .getDetails(NoCacheNoBackground) - .then((ok) => { - return resolve(); - }) - .catch((error) => { - console.error(`Background refresh error: ${error}`); - return resolve(); - }); - }); - } - const output = await repository.renameDefaultBranch(targetBranchName); - if (waitForRefresh) { - await finishUp(); - } else { - process.nextTick(() => { - finishUp() - .then((ok) => { - /* ignore */ - }) - .catch((error) => { - /* ignore */ - }); - }); - } - return { - message: `Branch renamed to ${targetBranchName} for ${repository.name}`, - output, - }; -} - -// Disabling this function as it is high risk: uses GHEC enterprise admin PAT and makes repo public outside of Org owner involvement -// router.post('/:repoName', asyncHandler(AddRepositoryPermissionsToRequest), asyncHandler(async function (req: ILocalRequest, res, next) { -// const repoPermissions = req.repoPermissions; -// if (!repoPermissions.allowAdministration) { -// return next(new Error('You do not have administrative permission on this repository')); -// } -// // only supporting the 'take public' operation now -// const takePublic = req.body['make-repo-public']; -// if (!takePublic) { -// return next(new Error('Unsupported operation')); -// } -// const repository = req.repository as Repository; -// await repository.editPublicPrivate({ private: false }); -// req.individualContext.webContext.saveUserAlert(`${repository.full_name} is now public.`, 'Repository publish', UserAlertType.Success); -// await repository.getDetails(NoCacheNoBackground); -// return res.redirect(`/${repository.organization.name}/repos/${repository.name}?published`); -// })); - router.get( '/:repoName', asyncHandler(AddRepositoryPermissionsToRequest), - asyncHandler(async function (req: ILocalRequest, res, next) { + asyncHandler(async function (req: ILocalRequest, res: Response, next: NextFunction) { const { linkProvider, config, graphProvider } = getProviders(req); const repoPermissions = req.repoPermissions; const referer = req.headers.referer as string; @@ -465,38 +379,6 @@ router.get( }) ); -router.get( - '/:repoName/defaultBranch', - asyncHandler(AddRepositoryPermissionsToRequest), - asyncHandler(async function (req: ILocalRequest, res, next) { - const referer = req.headers.referer as string; - const fromReposPage = referer && (referer.endsWith('repos') || referer.endsWith('repos/')); - const organization = req.organization; - const repoPermissions = req.repoPermissions; - const repository = req.repository; - const repositoryMetadataEntity = req.repositoryMetadata; - await repository.getDetails(); - const title = `${repository.name} - Default Branch Name`; - const details = await repository.organization.getDetails(); - const organizationSupportsUpdatesApp = await organization.supportsUpdatesApp(); - organization.id = details.id; - req.individualContext.webContext.render({ - view: 'repos/defaultBranch', - title, - state: { - organization, - organizationSupportsUpdatesApp, - repo: decorateRepoForView(repository), - reposSubView: 'defaultBranch', - repository, - fromReposPage, - repoPermissions, - repositoryMetadataEntity, - }, - }); - }) -); - export interface IRepositoryPermissionsView {} export async function calculateGroupedPermissionsViewForRepository(repository: Repository): Promise { @@ -506,7 +388,7 @@ export async function calculateGroupedPermissionsViewForRepository(repository: R collaborators, // Collaborator[] outsideCollaborators, // Collaborator[] } = await calculateRepoPermissions(organization, repository); - const systemTeams = combineAllTeams(organization.specialRepositoryPermissionTeams); // number[] + const systemTeams = combineAllTeams(organization.specialSystemTeams); // number[] const teamBasedPermissions = consolidateTeamPermissions(permissions, systemTeams); // busted? const groupedPermissions = slicePermissionsForView( filterSystemTeams(teamsFilterType.systemTeamsExcluded, systemTeams, permissions) @@ -549,7 +431,7 @@ export async function calculateGroupedPermissionsViewForRepository(repository: R router.get( '/:repoName/permissions', asyncHandler(AddRepositoryPermissionsToRequest), - asyncHandler(async function (req: ILocalRequest, res, next) { + asyncHandler(async function (req: ILocalRequest, res: Response, next: NextFunction) { const referer = req.headers.referer as string; const fromReposPage = referer && (referer.endsWith('repos') || referer.endsWith('repos/')); const organization = req.organization; @@ -563,7 +445,7 @@ router.get( organization, repository ); - const systemTeams = combineAllTeams(organization.specialRepositoryPermissionTeams); + const systemTeams = combineAllTeams(organization.specialSystemTeams); const teamBasedPermissions = consolidateTeamPermissions(permissions, systemTeams); const title = `${repository.name} - Repository`; const details = await repository.organization.getDetails(); @@ -599,7 +481,7 @@ router.get( router.get( '/:repoName/history', - asyncHandler(async function (req: ILocalRequest, res, next) { + asyncHandler(async function (req: ILocalRequest, res: Response, next: NextFunction) { const { auditLogRecordProvider } = getProviders(req); const referer = req.headers.referer as string; const fromReposPage = referer && (referer.endsWith('repos') || referer.endsWith('repos/')); @@ -754,7 +636,7 @@ function teamsToSet(teams) { return set; } -// function requireAdministration(req, res, next) { +// function requireAdministration(req, res: Response, next: NextFunction) { // const repoPermissions = req.repoPermissions; // if (!repoPermissions) { // return next(new Error('Not configured for repo permissions')); @@ -765,55 +647,4 @@ function teamsToSet(teams) { // return next(new Error('You are not authorized to administer this repository.')); // } -async function triggerRenameNotification( - providers: IProviders, - repository: Repository, - corporateUsername: string, - targetBranchName: string, - output: ITemporaryCommandOutput[] -): Promise { - const { config, insights, operations, mailAddressProvider, viewServices } = providers; - insights.trackMetric({ name: 'RenameDefaultBranches', value: 1 }); - insights.trackEvent({ - name: 'RenameDefaultBranch', - properties: { - orgName: repository.organization.name, - repoName: repository.name, - targetBranchName, - }, - }); - const mailAddress = await mailAddressProvider.getAddressFromUpn(corporateUsername); - const emailTemplate = 'repoDefaultBranchRenamed'; - const mail: IMail = { - to: [mailAddress], - cc: [operations.getInfrastructureNotificationsMail()], - subject: `${repository.organization.name}/${repository.name} default branch is now ${targetBranchName}`, - content: undefined, - }; - const contentOptions = { - reason: `You are receiving this e-mail as a transaction record from your action to rename the default branch of this repository you administer.`, - headline: `${targetBranchName} branch`, - notification: 'information', - app: config.brand?.companyName ? `${config.brand.companyName} GitHub` : 'GitHub', - output, - repository, - organization: repository.organization, - viewServices, - }; - try { - mail.content = await operations.emailRender(emailTemplate, contentOptions); - await operations.sendMail(mail); - } catch (mailError) { - console.warn(mailError); - insights.trackException({ - exception: mailError, - properties: { - repositoryName: repository.full_name, - organizationName: repository.organization.name, - eventName: 'SendRenameDefaultBranchMail', - }, - }); - } -} - export default router; diff --git a/routes/org/team/approval/index.ts b/routes/org/team/approval/index.ts index ffe17b298..33e06c147 100644 --- a/routes/org/team/approval/index.ts +++ b/routes/org/team/approval/index.ts @@ -3,11 +3,11 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; const router: Router = Router(); -import { ErrorHelper, getProviders } from '../../../../transitional'; +import { ErrorHelper, getProviders } from '../../../../lib/transitional'; import { Team } from '../../../../business'; import { PermissionWorkflowEngine } from '../approvals'; import RenderHtmlMail from '../../../../lib/emailRender'; @@ -62,7 +62,7 @@ router.get('/setNote/:action', function (req: ILocalRequest, res) { router.post( '/', - asyncHandler(async (req: ILocalRequest, res, next) => { + asyncHandler(async (req: ILocalRequest, res: Response, next: NextFunction) => { const providers = getProviders(req); const { individualContext } = req; const engine = req.approvalEngine as PermissionWorkflowEngine; diff --git a/routes/org/team/approvals.ts b/routes/org/team/approvals.ts index 88108ed18..7420ee162 100644 --- a/routes/org/team/approvals.ts +++ b/routes/org/team/approvals.ts @@ -3,16 +3,16 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; const router: Router = Router(); import RouteApproval from './approval'; -import { getProviders } from '../../../transitional'; -import { wrapError } from '../../../utils'; +import { getProviders } from '../../../lib/transitional'; +import { wrapError } from '../../../lib/utils'; import { Team } from '../../../business'; -import { TeamJoinApprovalEntity } from '../../../entities/teamJoinApproval/teamJoinApproval'; +import { TeamJoinApprovalEntity } from '../../../business/entities/teamJoinApproval/teamJoinApproval'; import { Account } from '../../../business'; import { ReposAppRequest, IRequestTeams } from '../../../interfaces'; @@ -91,14 +91,14 @@ export class PermissionWorkflowEngine { // Find the request and assign the workflow engine -router.use(function (req: ReposAppRequest, res, next) { +router.use(function (req: ReposAppRequest, res: Response, next: NextFunction) { req.individualContext.webContext.pushBreadcrumb('Approvals'); next(); }); router.get( '/', - asyncHandler(async (req: IRequestTeams, res, next) => { + asyncHandler(async (req: IRequestTeams, res: Response, next: NextFunction) => { const team = req.team2 as Team; const approvals = await team.getApprovals(); req.individualContext.webContext.render({ @@ -119,7 +119,7 @@ interface IRequestPlusApprovalEngine extends IRequestTeams { router.use( '/:requestid', - asyncHandler(async function (req: IRequestPlusApprovalEngine, res, next) { + asyncHandler(async function (req: IRequestPlusApprovalEngine, res: Response, next: NextFunction) { const team = req.team2 as Team; const requestid = req.params.requestid; const { approvalProvider, operations } = getProviders(req); diff --git a/routes/org/team/delete.ts b/routes/org/team/delete.ts index 92b05e2d6..9bd26af19 100644 --- a/routes/org/team/delete.ts +++ b/routes/org/team/delete.ts @@ -3,7 +3,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; const router: Router = Router(); import { ReposAppRequest, UserAlertType } from '../../../interfaces'; @@ -14,7 +14,7 @@ interface ILocalRequest extends ReposAppRequest { team2?: any; } -router.post('/', MiddlewareTeamAdminRequired, (req: ILocalRequest, res, next) => { +router.post('/', MiddlewareTeamAdminRequired, (req: ILocalRequest, res: Response, next: NextFunction) => { const organization = req.organization; const team2 = req.team2; team2.delete((error) => { diff --git a/routes/org/team/index-maintainer.ts b/routes/org/team/index-maintainer.ts index dba129c53..ef935c7c9 100644 --- a/routes/org/team/index-maintainer.ts +++ b/routes/org/team/index-maintainer.ts @@ -3,10 +3,10 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import { ReposAppRequest } from '../../../interfaces'; -import { wrapError } from '../../../utils'; +import { wrapError } from '../../../lib/utils'; const router: Router = Router(); import RouteApprovals from './approvals'; @@ -16,7 +16,7 @@ interface ILocalRequest extends ReposAppRequest { teamPermissions?: any; } -router.use(function (req: ILocalRequest, res, next) { +router.use(function (req: ILocalRequest, res: Response, next: NextFunction) { const teamPermissions = req.teamPermissions; if (!teamPermissions.allowAdministration) { const err = wrapError(null, 'You do not have permission to maintain this team.', true); diff --git a/routes/org/team/index.ts b/routes/org/team/index.ts index f96e01ee1..2b9def6f5 100644 --- a/routes/org/team/index.ts +++ b/routes/org/team/index.ts @@ -3,16 +3,16 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; const router: Router = Router(); import throat from 'throat'; -import { getProviders } from '../../../transitional'; -import { sortRepositoriesByNameCaseInsensitive, wrapError } from '../../../utils'; -import { TeamJoinApprovalEntity } from '../../../entities/teamJoinApproval/teamJoinApproval'; -import SelfServiceTeamMemberToMaintainerUpgrades from '../../../features/teamMemberToMaintainerUpgrade'; +import { getProviders } from '../../../lib/transitional'; +import { sortRepositoriesByNameCaseInsensitive, wrapError } from '../../../lib/utils'; +import { TeamJoinApprovalEntity } from '../../../business/entities/teamJoinApproval/teamJoinApproval'; +import SelfServiceTeamMemberToMaintainerUpgrades from '../../../business/features/teamMemberToMaintainerUpgrade'; import RouteMembers from './members'; import RouteReposPager from '../../reposPager'; import RouteDelete from './delete'; @@ -65,7 +65,7 @@ interface ILocalRequest extends ReposAppRequest { } router.use( - asyncHandler(async (req: ILocalRequest, res, next) => { + asyncHandler(async (req: ILocalRequest, res: Response, next: NextFunction) => { const { operations } = getProviders(req); const login = req.individualContext.getGitHubIdentity().username; const team2 = req.team2 as Team; @@ -93,7 +93,7 @@ router.use( ); router.use( - asyncHandler(async (req: ILocalRequest, res, next) => { + asyncHandler(async (req: ILocalRequest, res: Response, next: NextFunction) => { const { approvalProvider } = getProviders(req); const team2 = req.team2 as Team; if (!approvalProvider) { @@ -113,48 +113,53 @@ router.use( }) ); -router.use('/join', asyncHandler(AddOrganizationPermissionsToRequest), (req: ILocalRequest, res, next) => { - const organization = req.organization; - const team2 = req.team2; - const orgPermissions = req.orgPermissions; - - // Are they already a team member? - const currentMembershipStatus = req.membershipStatus; - if (currentMembershipStatus) { - return next( - wrapError(null, `You are already a ${currentMembershipStatus} of the ${team2.name} team`, true) - ); - } +router.use( + '/join', + asyncHandler(AddOrganizationPermissionsToRequest), + (req: ILocalRequest, res: Response, next: NextFunction) => { + const organization = req.organization; + const team2 = req.team2; + const orgPermissions = req.orgPermissions; + + // Are they already a team member? + const currentMembershipStatus = req.membershipStatus; + if (currentMembershipStatus) { + return next( + wrapError(null, `You are already a ${currentMembershipStatus} of the ${team2.name} team`, true) + ); + } - // Have they joined the organization yet? - const membershipStatus = orgPermissions.membershipStatus; - let error = null; - if (membershipStatus !== 'active') { - error = new Error(`You are not a member of the ${organization.name} GitHub organization.`); - error.title = 'Please join the organization before joining this team'; - error.detailed = - membershipStatus === 'pending' - ? 'You have not accepted your membership yet, or do not have two-factor authentication enabled.' - : 'After you join the organization, you can join this team.'; - error.skipOops = true; - error.skipLog = true; - error.fancyLink = { - link: `/${organization.name}`, - title: `Join the ${organization.name} organization`, - }; + // Have they joined the organization yet? + const membershipStatus = orgPermissions.membershipStatus; + let error = null; + if (membershipStatus !== 'active') { + error = new Error(`You are not a member of the ${organization.name} GitHub organization.`); + error.title = 'Please join the organization before joining this team'; + error.detailed = + membershipStatus === 'pending' + ? 'You have not accepted your membership yet, or do not have two-factor authentication enabled.' + : 'After you join the organization, you can join this team.'; + error.skipOops = true; + error.skipLog = true; + error.fancyLink = { + link: `/${organization.name}`, + title: `Join the ${organization.name} organization`, + }; + } + return next(error); } - return next(error); -}); +); router.get( '/join', - asyncHandler(async function (req: ILocalRequest, res, next) { + asyncHandler(async function (req: ILocalRequest, res: Response, next: NextFunction) { const team2 = req.team2 as Team; const organization = req.organization as Organization; - // The broad access "all members" team is always open for automatic joining without - // approval. This short circuit is to show that option. - const broadAccessTeams = new Set(organization.broadAccessTeams); - if (broadAccessTeams.has(team2.id)) { + // The broad access "all members" team and any "easy access" teams are + // always open for automatic joining without approval. This short circuit + // is to show that option. + const allowSelfJoinTeams = new Set([...organization.broadAccessTeams, ...organization.openAccessTeams]); + if (allowSelfJoinTeams.has(team2.id)) { req.individualContext.webContext.render({ view: 'org/team/join', title: `Join ${team2.name}`, @@ -181,7 +186,7 @@ router.get( router.post( '/selfServiceMaintainerUpgrade', - asyncHandler(async (req: ILocalRequest, res, next) => { + asyncHandler(async (req: ILocalRequest, res: Response, next: NextFunction) => { const { selfServiceTeamMemberToMaintainerUpgrades } = req; if (!selfServiceTeamMemberToMaintainerUpgrades) { throw new Error('System not available'); @@ -214,7 +219,7 @@ export interface ITeamJoinRequestSubmitOutcome { router.post( '/join', - asyncHandler(async (req: ILocalRequest, res, next) => { + asyncHandler(async (req: ILocalRequest, res: Response, next: NextFunction) => { if (req.existingRequest) { throw new Error('You have already created a team join request that is pending a decision.'); } @@ -251,7 +256,8 @@ export async function submitTeamJoinRequest( ): Promise { const { approvalProvider, config, graphProvider, mailProvider, insights, operations } = providers; const organization = team.organization; - const broadAccessTeams = new Set(organization.broadAccessTeams); + const { broadAccessTeams, openAccessTeams } = organization; + const allowSelfJoinTeams = new Set([...broadAccessTeams, ...openAccessTeams]); if (!approvalProvider) { return { error: new Error('No approval provider available') }; } @@ -259,8 +265,17 @@ export async function submitTeamJoinRequest( if (!username) { return { error: new Error('Active context required') }; } - if (broadAccessTeams.has(team.id)) { + if (allowSelfJoinTeams.has(team.id)) { try { + const eventName = broadAccessTeams.includes(team.id) ? 'JoinBroadAccessTeam' : 'JoinOpenAccessTeam'; + insights?.trackEvent({ + name: eventName, + properties: { + organization: organization.name, + id: team.id, + slug: team.slug, + }, + }); await team.addMembership(username); } catch (error) { insights?.trackEvent({ @@ -502,7 +517,7 @@ export async function submitTeamJoinRequest( router.use(asyncHandler(AddTeamPermissionsToRequest)); // The view uses this information today to show the sudo banner -router.use((req: ILocalRequest, res, next) => { +router.use((req: ILocalRequest, res: Response, next: NextFunction) => { if (req.teamPermissions.sudo === true) { req.sudoMode = true; } @@ -692,14 +707,18 @@ async function basicTeamsView(req: ILocalRequest, display: BasicTeamViewPage) { }); } -router.get('/', asyncHandler(AddOrganizationPermissionsToRequest), async (req: ILocalRequest, res, next) => { - await basicTeamsView(req, BasicTeamViewPage.Default); -}); +router.get( + '/', + asyncHandler(AddOrganizationPermissionsToRequest), + async (req: ILocalRequest, res: Response, next: NextFunction) => { + await basicTeamsView(req, BasicTeamViewPage.Default); + } +); router.get( '/history', asyncHandler(AddOrganizationPermissionsToRequest), - async (req: ILocalRequest, res, next) => { + async (req: ILocalRequest, res: Response, next: NextFunction) => { await basicTeamsView(req, BasicTeamViewPage.History); } ); @@ -707,7 +726,7 @@ router.get( router.get( '/repositories', asyncHandler(AddOrganizationPermissionsToRequest), - async (req: ILocalRequest, res, next) => { + async (req: ILocalRequest, res: Response, next: NextFunction) => { await basicTeamsView(req, BasicTeamViewPage.Repositories); } ); diff --git a/routes/org/team/leave.ts b/routes/org/team/leave.ts index 2c4f9eb67..1b6e20366 100644 --- a/routes/org/team/leave.ts +++ b/routes/org/team/leave.ts @@ -3,7 +3,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; const router: Router = Router(); @@ -17,7 +17,7 @@ interface ILocalRequest extends ReposAppRequest { router.post( '/', - asyncHandler(async (req: ILocalRequest, res, next) => { + asyncHandler(async (req: ILocalRequest, res: Response, next: NextFunction) => { const organization = req.organization as Organization; const team2 = req.team2 as Team; const username = req.individualContext.link.thirdPartyUsername; diff --git a/routes/org/team/maintainers.ts b/routes/org/team/maintainers.ts index d8ea7ff44..e4a42e22d 100644 --- a/routes/org/team/maintainers.ts +++ b/routes/org/team/maintainers.ts @@ -3,11 +3,11 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; const router: Router = Router(); -import { getProviders, validateGitHubLogin } from '../../../transitional'; +import { getProviders, validateGitHubLogin } from '../../../lib/transitional'; import { ReposAppRequest, RequestTeamMemberAddType, @@ -28,7 +28,7 @@ interface ILocalRequest extends ReposAppRequest { } router.use( - asyncHandler(async (req: ILocalRequest, res, next) => { + asyncHandler(async (req: ILocalRequest, res: Response, next: NextFunction) => { // Get the latest maintainers, forced, with every request const team2 = req.team2 as Team; const maintainers = await refreshMaintainers(team2); @@ -51,7 +51,7 @@ router.get('/refresh', (req: ILocalRequest, res) => { router.post( '/:id/downgrade', MiddlewareTeamAdminRequired, - asyncHandler(async (req: ILocalRequest, res, next) => { + asyncHandler(async (req: ILocalRequest, res: Response, next: NextFunction) => { const team2 = req.team2 as Team; const id = req.params.id; const verifiedCurrentMaintainers = req.verifiedCurrentMaintainers; @@ -82,7 +82,7 @@ router.post( }) ); -router.use('/add', MiddlewareTeamAdminRequired, (req: ILocalRequest, res, next) => { +router.use('/add', MiddlewareTeamAdminRequired, (req: ILocalRequest, res: Response, next: NextFunction) => { req.team2AddType = RequestTeamMemberAddType.Maintainer; return next(); }); @@ -90,7 +90,7 @@ router.use('/add', MiddlewareTeamAdminRequired, (req: ILocalRequest, res, next) router.post( '/add', MiddlewareTeamAdminRequired, - asyncHandler(async function (req: ILocalRequest, res, next) { + asyncHandler(async function (req: ILocalRequest, res: Response, next: NextFunction) { const { operations } = getProviders(req); const login = validateGitHubLogin(req.body.username); const team2 = req.team2 as Team; diff --git a/routes/org/team/members.ts b/routes/org/team/members.ts index d9d911ca4..68339a928 100644 --- a/routes/org/team/members.ts +++ b/routes/org/team/members.ts @@ -3,13 +3,13 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; const router: Router = Router(); import { Team, TeamMember } from '../../../business'; import { ReposAppRequest, RequestTeamMemberAddType, UserAlertType } from '../../../interfaces'; -import { getProviders, validateGitHubLogin } from '../../../transitional'; +import { getProviders, validateGitHubLogin } from '../../../lib/transitional'; import RoutePeopleSearch from '../../peopleSearch'; import MiddlewareTeamAdminRequired from './teamAdminRequired'; @@ -55,7 +55,7 @@ async function refreshMembersAndSummary(team2: Team, when): Promise { } router.use( - asyncHandler(async (req: ILocalTeamRequest, res, next) => { + asyncHandler(async (req: ILocalTeamRequest, res: Response, next: NextFunction) => { // Always make sure to have a relatively up-to-date membership cache available const team2 = req.team2 as Team; req.refreshedMembers = await refreshMembers( @@ -70,7 +70,7 @@ router.use( router.get( '/refresh', - asyncHandler(async (req: ILocalTeamRequest, res, next) => { + asyncHandler(async (req: ILocalTeamRequest, res: Response, next: NextFunction) => { // Refresh all the pages and also the cached single-page view shown on the team page const team2 = req.team2 as Team; await refreshMembersAndSummary(team2, 'whenever'); @@ -81,7 +81,7 @@ router.get( // Browse members router.use( '/browse', - (req: ILocalTeamRequest, res, next) => { + (req: ILocalTeamRequest, res: Response, next: NextFunction) => { req.team2RemoveType = 'member'; return next(); }, @@ -92,7 +92,7 @@ router.use( router.use( '/add', MiddlewareTeamAdminRequired, - (req: ILocalTeamRequest, res, next) => { + (req: ILocalTeamRequest, res: Response, next: NextFunction) => { req.team2AddType = RequestTeamMemberAddType.Member; return next(); }, @@ -102,7 +102,7 @@ router.use( router.post( '/remove', MiddlewareTeamAdminRequired, - asyncHandler(async (req: ILocalTeamRequest, res, next) => { + asyncHandler(async (req: ILocalTeamRequest, res: Response, next: NextFunction) => { const { operations } = getProviders(req); const username = validateGitHubLogin(req.body.username); const team2 = req.team2 as Team; @@ -120,7 +120,7 @@ router.post( router.post( '/add', MiddlewareTeamAdminRequired, - asyncHandler(async (req: ILocalTeamRequest, res, next) => { + asyncHandler(async (req: ILocalTeamRequest, res: Response, next: NextFunction) => { const { operations } = getProviders(req); const username = validateGitHubLogin(req.body.username); const organization = req.organization; diff --git a/routes/org/team/properties.ts b/routes/org/team/properties.ts index 0843c67ed..63f792b36 100644 --- a/routes/org/team/properties.ts +++ b/routes/org/team/properties.ts @@ -3,11 +3,11 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; const router: Router = Router(); import { ReposAppRequest, UserAlertType } from '../../../interfaces'; -import { wrapError } from '../../../utils'; +import { wrapError } from '../../../lib/utils'; import MiddlewareTeamAdminRequired from './teamAdminRequired'; @@ -16,48 +16,56 @@ interface IRequestWithTeamAndLegacy extends ReposAppRequest { teamUrl?: string; } -router.get('/', MiddlewareTeamAdminRequired, (req: IRequestWithTeamAndLegacy, res, next) => { - const team2 = req.team2; - team2.getDetails((error) => { - if (error) { - return next(wrapError(error, 'Had trouble getting the detailed properties for this team.')); - } - req.individualContext.webContext.pushBreadcrumb('Properties'); - req.individualContext.webContext.render({ - view: 'org/team/properties', - title: team2.name + ' - Properties', - state: { - team: team2, - teamUrl: req.teamUrl, - }, +router.get( + '/', + MiddlewareTeamAdminRequired, + (req: IRequestWithTeamAndLegacy, res: Response, next: NextFunction) => { + const team2 = req.team2; + team2.getDetails((error) => { + if (error) { + return next(wrapError(error, 'Had trouble getting the detailed properties for this team.')); + } + req.individualContext.webContext.pushBreadcrumb('Properties'); + req.individualContext.webContext.render({ + view: 'org/team/properties', + title: team2.name + ' - Properties', + state: { + team: team2, + teamUrl: req.teamUrl, + }, + }); }); - }); -}); + } +); -router.post('/', MiddlewareTeamAdminRequired, (req: IRequestWithTeamAndLegacy, res, next) => { - const team2 = req.team2; - const organization = req.organization; - const patch = { - name: req.body.ghname, - description: req.body.description, - }; - team2.edit(patch, (error) => { - if (error) { - return next(error); - } - req.individualContext.webContext.saveUserAlert( - 'Team properties updated on GitHub', - 'Properties Saved', - UserAlertType.Success - ); - team2.getDetails((getDetailsError) => { - if (getDetailsError) { - return next(getDetailsError); +router.post( + '/', + MiddlewareTeamAdminRequired, + (req: IRequestWithTeamAndLegacy, res: Response, next: NextFunction) => { + const team2 = req.team2; + const organization = req.organization; + const patch = { + name: req.body.ghname, + description: req.body.description, + }; + team2.edit(patch, (error) => { + if (error) { + return next(error); } - const slug = team2.slug; - return res.redirect('/' + organization.name + '/teams/' + slug); + req.individualContext.webContext.saveUserAlert( + 'Team properties updated on GitHub', + 'Properties Saved', + UserAlertType.Success + ); + team2.getDetails((getDetailsError) => { + if (getDetailsError) { + return next(getDetailsError); + } + const slug = team2.slug; + return res.redirect('/' + organization.name + '/teams/' + slug); + }); }); - }); -}); + } +); export default router; diff --git a/routes/org/team/teamAdminRequired.ts b/routes/org/team/teamAdminRequired.ts index 748ca8d51..6c40a9667 100644 --- a/routes/org/team/teamAdminRequired.ts +++ b/routes/org/team/teamAdminRequired.ts @@ -3,9 +3,11 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +import { NextFunction, Response } from 'express'; + import { IReposError } from '../../../interfaces'; -export default function middlewareTeamAdminRequired(req, res, next) { +export default function middlewareTeamAdminRequired(req, res: Response, next: NextFunction) { const teamPermissions = req.teamPermissions; if (!teamPermissions) { return next(new Error('No team permissions information available')); diff --git a/routes/org/teams.ts b/routes/org/teams.ts index 74fc0bfd0..936e5ae5d 100644 --- a/routes/org/teams.ts +++ b/routes/org/teams.ts @@ -3,12 +3,12 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; const router: Router = Router(); import { ReposAppRequest } from '../../interfaces'; -import { popSessionVariable } from '../../utils'; +import { popSessionVariable } from '../../lib/utils'; import lowercaser from '../../middleware/lowercaser'; import RouteTeam from './team/'; @@ -19,7 +19,7 @@ interface ITeamsRequest extends ReposAppRequest { teamUrl?: any; } -router.use(function (req: ReposAppRequest, res, next) { +router.use(function (req: ReposAppRequest, res: Response, next: NextFunction) { req.individualContext.webContext.pushBreadcrumb('Teams'); req.reposContext = { section: 'teams', @@ -28,7 +28,7 @@ router.use(function (req: ReposAppRequest, res, next) { next(); }); -router.get('/', function (req, res, next) { +router.get('/', function (req, res: Response, next: NextFunction) { const beforeLinkReferrer = popSessionVariable(req, res, 'beforeLinkReferrer'); if (beforeLinkReferrer !== undefined) { return res.redirect(beforeLinkReferrer); @@ -40,7 +40,7 @@ router.get('/', lowercaser(['sort', 'set']), RouteTeamsPager); router.use( '/:teamSlug', - asyncHandler(async (req: ITeamsRequest, res, next) => { + asyncHandler(async (req: ITeamsRequest, res: Response, next: NextFunction) => { const organization = req.organization; const orgBaseUrl = organization.baseUrl; const slug = req.params.teamSlug as string; diff --git a/routes/orgAdmin.ts b/routes/orgAdmin.ts index f947ea224..9cc992d03 100644 --- a/routes/orgAdmin.ts +++ b/routes/orgAdmin.ts @@ -3,11 +3,11 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; const router: Router = Router(); -import { getProviders } from '../transitional'; +import { getProviders } from '../lib/transitional'; import { requirePortalAdministrationPermission } from '../middleware/business/administration'; import { PostgresLinkProvider } from '../lib/linkProviders/postgres/postgresLinkProvider'; @@ -16,7 +16,7 @@ import { Organization } from '../business'; import { Account } from '../business'; import { ILinkProvider } from '../lib/linkProviders'; import { ICorporateLink, ReposAppRequest, IProviders, UnlinkPurpose } from '../interfaces'; -import { isCodespacesAuthenticating } from '../utils'; +import { isCodespacesAuthenticating } from '../lib/utils'; // - - - Middleware: require that the user isa portal administrator to continue router.use(requirePortalAdministrationPermission); @@ -25,9 +25,11 @@ router.use(requirePortalAdministrationPermission); // These functions are not pretty. enum OperationsAction { - DestroyLink, - MarkAsServiceAccount, - UnmarkServiceAccount, + DestroyLink = 'Destroy link', + MarkAsServiceAccount = 'Mark as service account', + UnmarkServiceAccount = 'Unmark service account', + DestroyCollaboratorGrants = 'Destroy collaborator grants', + Destroy100 = 'Destroy 100', } enum UserQueryByType { @@ -207,10 +209,13 @@ async function loadInformation( if (queryCache && queryCache.supportsRepositoryCollaborators) { const result = await queryCache.userCollaboratorRepositories(thirdPartyId); const collaboratorRepositories = []; + const hasMany = result.length > 100; for (const { repository } of result) { try { - await repository.getDetails(); - collaboratorRepositories.push(repository.full_name); + if (!hasMany) { + await repository.getDetails(); + } + collaboratorRepositories.push(repository.organization.name + '/' + repository.name); } catch (ignoreError) { console.dir(ignoreError); } @@ -264,7 +269,7 @@ async function getGitHubAccountInformationById(operations: Operations, id: strin return account; } -router.get('/whois/id/:githubid', function (req: ReposAppRequest, res, next) { +router.get('/whois/id/:githubid', function (req: ReposAppRequest, res: Response, next: NextFunction) { const thirdPartyId = req.params.githubid; const providers = getProviders(req); queryByGitHubId(providers, thirdPartyId) @@ -296,7 +301,7 @@ interface IIDValue { router.get( '/whois/link/:linkid', - asyncHandler(async function (req: ReposAppRequest, res, next) { + asyncHandler(async function (req: ReposAppRequest, res: Response, next: NextFunction) { const linkId = req.params.linkid; const { linkProvider: lp } = getProviders(req); const linkProvider = lp as PostgresLinkProvider; @@ -315,7 +320,7 @@ router.get( router.post( '/whois/link/:linkid', - asyncHandler(async function (req: ReposAppRequest, res, next) { + asyncHandler(async function (req: ReposAppRequest, res: Response, next: NextFunction) { const { config } = getProviders(req); const linkId = req.params.linkid; const isLinkDelete = req.body['delete-link']; @@ -383,7 +388,7 @@ router.post( router.post( '/whois/link/', - asyncHandler(async function (req: ReposAppRequest, res, next) { + asyncHandler(async function (req: ReposAppRequest, res: Response, next: NextFunction) { const { config, operations } = getProviders(req); const allowAdministratorManualLinking = operations?.config?.features?.allowAdministratorManualLinking; if (!allowAdministratorManualLinking) { @@ -436,13 +441,17 @@ router.post( }) ); -router.post('/whois/id/:githubid', function (req: ReposAppRequest, res, next) { +router.post('/whois/id/:githubid', function (req: ReposAppRequest, res: Response, next: NextFunction) { const thirdPartyId = req.params.githubid; const markAsServiceAccount = req.body['mark-as-service-account']; const unmarkServiceAccount = req.body['unmark-service-account']; + const removeCollaboration = req.body['remove-collaboration'] || req.body['remove-collaboration-100']; + const remove100 = req.body['remove-collaboration-100']; const providers = getProviders(req); let action = OperationsAction.DestroyLink; - if (markAsServiceAccount) { + if (removeCollaboration) { + action = remove100 ? OperationsAction.Destroy100 : OperationsAction.DestroyCollaboratorGrants; + } else if (markAsServiceAccount) { action = OperationsAction.MarkAsServiceAccount; } else if (unmarkServiceAccount) { action = OperationsAction.UnmarkServiceAccount; @@ -458,7 +467,7 @@ router.post('/whois/id/:githubid', function (req: ReposAppRequest, res, next) { } req.individualContext.webContext.render({ view: 'organization/whois/drop', - title: `Dropped link by ID ${thirdPartyId}`, + title: `${action} link by ID ${thirdPartyId}`, state, }); }) @@ -467,7 +476,7 @@ router.post('/whois/id/:githubid', function (req: ReposAppRequest, res, next) { }); }); -router.get('/whois/aad/:upn', function (req: ReposAppRequest, res, next) { +router.get('/whois/aad/:upn', function (req: ReposAppRequest, res: Response, next: NextFunction) { const upn = req.params.upn; const providers = getProviders(req); queryByCorporateUsername(providers, upn) @@ -488,7 +497,7 @@ router.get('/whois/aad/:upn', function (req: ReposAppRequest, res, next) { .catch(next); }); -router.get('/whois/github/:username', function (req: ReposAppRequest, res, next) { +router.get('/whois/github/:username', function (req: ReposAppRequest, res: Response, next: NextFunction) { const login = req.params.username; const providers = getProviders(req); queryByGitHubLogin(providers, login) @@ -507,13 +516,17 @@ router.get('/whois/github/:username', function (req: ReposAppRequest, res, next) .catch(next); }); -router.post('/whois/github/:username', function (req: ReposAppRequest, res, next) { +router.post('/whois/github/:username', function (req: ReposAppRequest, res: Response, next: NextFunction) { const username = req.params.username; const markAsServiceAccount = req.body['mark-as-service-account']; const unmarkServiceAccount = req.body['unmark-service-account']; + const removeCollaboration = req.body['remove-collaboration'] || req.body['remove-collaboration-100']; + const remove100 = req.body['remove-collaboration-100']; const providers = getProviders(req); let action = OperationsAction.DestroyLink; - if (markAsServiceAccount) { + if (removeCollaboration) { + action = remove100 ? OperationsAction.Destroy100 : OperationsAction.DestroyCollaboratorGrants; + } else if (markAsServiceAccount) { action = OperationsAction.MarkAsServiceAccount; } else if (unmarkServiceAccount) { action = OperationsAction.UnmarkServiceAccount; @@ -630,6 +643,13 @@ async function destructiveLogic( ); } + if (action === OperationsAction.DestroyCollaboratorGrants || action === OperationsAction.Destroy100) { + const account: Account = operations.getAccount(thirdPartyId); + const res = await account.removeCollaboratorPermissions(action === OperationsAction.Destroy100); + state.messages = res.history; + return state; + } + // Account termination if (linkQuery && linkQuery.link && !thirdPartyId) { thirdPartyId = linkQuery.link.thirdPartyId; @@ -672,7 +692,7 @@ router.get('/bulkRepoDelete', (req: ReposAppRequest, res) => { router.post( '/bulkRepoDelete', - asyncHandler(async (req: ReposAppRequest, res, next) => { + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { const { operations } = getProviders(req); let repositories = req.body.repositories; // TODO: FEATURE FLAG: add a feature flag whether this API is available. @@ -708,7 +728,7 @@ router.post( log.push(`Skipping, does not appear to be a GitHub repo URL: ${repositoryName}`); } } - return res.json(log); + return res.json(log) as unknown as void; }) ); diff --git a/routes/orgs.ts b/routes/orgs.ts index 3a0750280..08e812639 100644 --- a/routes/orgs.ts +++ b/routes/orgs.ts @@ -5,14 +5,14 @@ import querystring from 'querystring'; -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; const router: Router = Router(); import { IReposRequestWithOrganization } from '../interfaces'; import { injectReactClient, TryFallbackToBlob } from '../middleware'; -import { getProviders, hasStaticReactClientApp } from '../transitional'; -import { wrapError } from '../utils'; +import { getProviders, hasStaticReactClientApp } from '../lib/transitional'; +import { wrapError } from '../lib/utils'; import orgRoute from './org/'; @@ -26,7 +26,11 @@ if (hasReactApp) { router.use('/:orgName', asyncHandler(forwardToOrganizationRoutes)); -async function forwardToOrganizationRoutes(req: IReposRequestWithOrganization, res, next) { +async function forwardToOrganizationRoutes( + req: IReposRequestWithOrganization, + res: Response, + next: NextFunction +) { // This middleware contains both the original GitHub operations types // as well as the newer implementation. In time this will peel apart. const orgName = req.params.orgName; diff --git a/routes/people.ts b/routes/people.ts index 1f9e82ce4..b2592cadb 100644 --- a/routes/people.ts +++ b/routes/people.ts @@ -3,16 +3,16 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; const router: Router = Router(); -import { getProviders } from '../transitional'; +import { getProviders } from '../lib/transitional'; import { ReposAppRequest } from '../interfaces'; import RoutePeopleSearch from './peopleSearch'; import MiddlewareSystemWidePermissions from '../middleware/github/systemWidePermissions'; -router.use(function (req: ReposAppRequest, res, next) { +router.use(function (req: ReposAppRequest, res: Response, next: NextFunction) { req.individualContext.webContext.pushBreadcrumb('People'); req.reposContext = { section: 'people', @@ -22,7 +22,7 @@ router.use(function (req: ReposAppRequest, res, next) { }); // Campaign-related redirect to take the user to GitHub -router.get('/github/:login', (req: ReposAppRequest, res, next) => { +router.get('/github/:login', (req: ReposAppRequest, res: Response, next: NextFunction) => { const providers = getProviders(req); if (!providers || !providers.campaign) { return next(); diff --git a/routes/peopleSearch.ts b/routes/peopleSearch.ts index 51c2746e7..023c08b60 100644 --- a/routes/peopleSearch.ts +++ b/routes/peopleSearch.ts @@ -3,11 +3,11 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; const router: Router = Router(); -import { getProviders } from '../transitional'; +import { getProviders } from '../lib/transitional'; import { RequestWithSystemwidePermissions, RequestTeamMemberAddType } from '../interfaces'; import { ensureAllLinksInMemory, getAllLinksFromRequest } from '../middleware/business/allLinks'; @@ -75,7 +75,7 @@ async function getPeopleAcrossOrganizations( router.get( '/', lowercaser(['sort']), - asyncHandler(async (req: IPeopleSearchRequest, res, next) => { + asyncHandler(async (req: IPeopleSearchRequest, res: Response, next: NextFunction) => { const linksFromMiddleware = getAllLinksFromRequest(req); const { operations } = getProviders(req); const org = req.organization ? req.organization.name : null; diff --git a/routes/placeholders.ts b/routes/placeholders.ts index cf28a6626..bb566fa43 100644 --- a/routes/placeholders.ts +++ b/routes/placeholders.ts @@ -3,11 +3,11 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; const router: Router = Router(); import { ReposAppRequest } from '../interfaces'; -import { getProviders } from '../transitional'; +import { getProviders } from '../lib/transitional'; // These are Microsoft-specific, we'll remove these eventually. // TODO: remove from open source version since not helpful having random routes in place diff --git a/routes/releasesSpa.ts b/routes/releasesSpa.ts index fb683135f..a80cee5f5 100644 --- a/routes/releasesSpa.ts +++ b/routes/releasesSpa.ts @@ -3,7 +3,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import { ReposAppRequest } from '../interfaces'; const router: Router = Router(); diff --git a/routes/repos.ts b/routes/repos.ts index 8d0daffb5..a8a0f7c2c 100644 --- a/routes/repos.ts +++ b/routes/repos.ts @@ -3,7 +3,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; const router: Router = Router(); import { ReposAppRequest } from '../interfaces'; @@ -11,7 +11,7 @@ import lowercaser from '../middleware/lowercaser'; import RouteReposPager from './reposPager'; -router.use(function (req: ReposAppRequest, res, next) { +router.use(function (req: ReposAppRequest, res: Response, next: NextFunction) { req.individualContext.webContext.pushBreadcrumb('Repositories'); req.reposContext = { section: 'repos', diff --git a/routes/reposPager.ts b/routes/reposPager.ts index a44500fb9..17e5c7fb2 100644 --- a/routes/reposPager.ts +++ b/routes/reposPager.ts @@ -7,7 +7,7 @@ import asyncHandler from 'express-async-handler'; import { NextFunction, Response } from 'express'; import _ from 'lodash'; -import { daysInMilliseconds } from '../utils'; +import { daysInMilliseconds } from '../lib/utils'; import { Repository, IPersonalizedUserAggregateRepositoryPermission, @@ -20,7 +20,7 @@ import { import QueryCache from '../business/queryCache'; import { GitHubRepositoryType, IReposAppWithTeam } from '../interfaces'; import { IRequestTeamPermissions } from '../middleware/github/teamPermissions'; -import { getProviders } from '../transitional'; +import { getProviders } from '../lib/transitional'; import { UserContext } from '../business/user/aggregate'; interface IGetReposAndOptionalTeamPermissionsResponse { @@ -213,7 +213,7 @@ export default asyncHandler(async function (req: IReposAppWithTeam, res: Respons await search.search(page, req.query.sort as string); - // await Promise.all(search.repos.map(repo => repo.getDetails())); + await Promise.all(search.repos.map(repo => repo.getDetails())); req.individualContext.webContext.render({ view: 'repos/', diff --git a/routes/settings/approvals.ts b/routes/settings/approvals.ts index aa150ee3c..e71c73bbc 100644 --- a/routes/settings/approvals.ts +++ b/routes/settings/approvals.ts @@ -3,19 +3,19 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; const router: Router = Router(); -import { IApprovalProvider } from '../../entities/teamJoinApproval/approvalProvider'; -import { TeamJoinApprovalEntity } from '../../entities/teamJoinApproval/teamJoinApproval'; -import { safeLocalRedirectUrl } from '../../utils'; +import { IApprovalProvider } from '../../business/entities/teamJoinApproval/approvalProvider'; +import { TeamJoinApprovalEntity } from '../../business/entities/teamJoinApproval/teamJoinApproval'; +import { safeLocalRedirectUrl } from '../../lib/utils'; import { Operations } from '../../business'; import { Team } from '../../business'; import { Organization } from '../../business'; import { IAggregateUserTeams } from '../../business/user/aggregate'; import { ReposAppRequest, IReposError, UserAlertType } from '../../interfaces'; -import { getProviders } from '../../transitional'; +import { getProviders } from '../../lib/transitional'; export interface ApprovalPair { team: Team; @@ -82,7 +82,7 @@ export async function Approvals_getUserRequests( router.get( '/', - asyncHandler(async function (req: ReposAppRequest, res, next) { + asyncHandler(async function (req: ReposAppRequest, res: Response, next: NextFunction) { const { approvalProvider, operations } = getProviders(req); if (!approvalProvider) { return next(new Error('No approval provider instance available')); @@ -107,7 +107,7 @@ router.get( }) ); -router.post('/:requestid/cancel', function (req: ReposAppRequest, res, next) { +router.post('/:requestid/cancel', function (req: ReposAppRequest, res: Response, next: NextFunction) { const { approvalProvider } = getProviders(req); if (!approvalProvider) { return next(new Error('No approval provider instance available')); @@ -143,7 +143,7 @@ router.post('/:requestid/cancel', function (req: ReposAppRequest, res, next) { router.get( '/:requestid', - asyncHandler(async function (req: ReposAppRequest, res, next) { + asyncHandler(async function (req: ReposAppRequest, res: Response, next: NextFunction) { const requestid = req.params.requestid; const { approvalProvider, operations } = getProviders(req); req.individualContext.webContext.pushBreadcrumb('Your Request'); diff --git a/routes/settings/authorizations.ts b/routes/settings/authorizations.ts index 1cd16c480..17aae9bec 100644 --- a/routes/settings/authorizations.ts +++ b/routes/settings/authorizations.ts @@ -3,11 +3,11 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; const router: Router = Router(); -import { getProviders } from '../../transitional'; +import { getProviders } from '../../lib/transitional'; import { Operations } from '../../business'; import { ReposAppRequest, ICorporateLink } from '../../interfaces'; @@ -54,7 +54,7 @@ function createValidator(operations: Operations, link: ICorporateLink, token: st }; } -router.use((req: IRequestWithAuthorizations, res, next) => { +router.use((req: IRequestWithAuthorizations, res: Response, next: NextFunction) => { // This is a lightweight, temporary implementation of authorization management to help clear // stored session tokens for apps like GitHub, VSTS, etc. const { operations } = getProviders(req); @@ -113,7 +113,7 @@ router.get('/', (req: IRequestWithAuthorizations, res) => { router.get( '/validate', - asyncHandler(async (req: IRequestWithAuthorizations, res, next) => { + asyncHandler(async (req: IRequestWithAuthorizations, res: Response, next: NextFunction) => { const authorizations = req.authorizations; for (const authorization of authorizations) { const validator = authorization.validator; diff --git a/routes/settings/campaigns.ts b/routes/settings/campaigns.ts index a5c7b5ef3..f2c8cb7aa 100644 --- a/routes/settings/campaigns.ts +++ b/routes/settings/campaigns.ts @@ -3,12 +3,12 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; const router: Router = Router(); import { ReposAppRequest, UserAlertType } from '../../interfaces'; -import { CreateError, getProviders } from '../../transitional'; +import { CreateError, getProviders } from '../../lib/transitional'; router.use('/:campaignGroupId', (req: ReposAppRequest, res: any, next) => { const { config } = getProviders(req); @@ -25,21 +25,21 @@ router.use('/:campaignGroupId', (req: ReposAppRequest, res: any, next) => { router.get( '/:campaignGroupId/unsubscribe', - asyncHandler(async (req: ReposAppRequest, res: any, next) => { + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { return await modifySubscription(true, req, res, next); }) ); router.get( '/:campaignGroupId/subscribe', - asyncHandler(async (req: ReposAppRequest, res: any, next) => { + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { return await modifySubscription(false, req, res, next); }) ); router.get( '/:campaignGroupId', - asyncHandler(async (req: ReposAppRequest, res: any, next) => { + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { const { campaignStateProvider } = getProviders(req); if (!campaignStateProvider) { return next(new Error('This app is not configured for campaign management')); @@ -53,7 +53,7 @@ router.get( return next(new Error('Corporate authentication and identity required')); } const currentState = await campaignStateProvider.getState(corporateId, campaignGroupId); - return res.json(currentState); + return res.json(currentState) as unknown as void; }) ); diff --git a/routes/settings/contributionData.ts b/routes/settings/contributionData.ts index 7e32f98f6..c68fc70b4 100644 --- a/routes/settings/contributionData.ts +++ b/routes/settings/contributionData.ts @@ -3,43 +3,17 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; const router: Router = Router(); -import { ErrorHelper, getProviders } from '../../transitional'; -import { UserSettings } from '../../entities/userSettings'; -import { ReposAppRequest, UserAlertType } from '../../interfaces'; +import { ErrorHelper, getProviders } from '../../lib/transitional'; +import { UserSettings } from '../../business/entities/userSettings'; +import { UserAlertType } from '../../interfaces'; +import type { ReposAppRequestWithUserSettings } from '../../interfaces/middleware'; +import { getUserSettings } from '../../middleware/business/userSettings'; -export interface IRequestWithUserSettings extends ReposAppRequest { - userSettings?: UserSettings; -} - -async function getSettings(req: IRequestWithUserSettings, res, next) { - const corporateId = req.individualContext.corporateIdentity.id; - const { userSettingsProvider } = getProviders(req); - if (!req.userSettings) { - let settings: UserSettings = null; - try { - settings = await userSettingsProvider.getUserSettings(corporateId); - } catch (notFoundError) { - if (ErrorHelper.IsNotFound(notFoundError)) { - // ignore - } else { - throw notFoundError; - } - } - if (!settings) { - settings = new UserSettings(); - settings.corporateId = corporateId; - await userSettingsProvider.insertUserSettings(settings); - } - req.userSettings = settings; - } - return next(); -} - -function view(req: IRequestWithUserSettings, res) { +function view(req: ReposAppRequestWithUserSettings, res) { const userSettings = req.userSettings; req.individualContext.webContext.render({ view: 'settings/contributionData', @@ -50,13 +24,13 @@ function view(req: IRequestWithUserSettings, res) { }); } -router.use(asyncHandler(getSettings)); +router.use(asyncHandler(getUserSettings)); router.get('/', view); router.post( '/', - asyncHandler(async function (req: IRequestWithUserSettings, res, next) { + asyncHandler(async function (req: ReposAppRequestWithUserSettings, res: Response, next: NextFunction) { const isOptIn = !!(req.body.optIn === '1'); const currentSetting = req.userSettings.contributionShareOptIn; req.userSettings.contributionShareOptIn = isOptIn; diff --git a/routes/settings/index.ts b/routes/settings/index.ts index 37ea8f92b..8b25be30a 100644 --- a/routes/settings/index.ts +++ b/routes/settings/index.ts @@ -3,7 +3,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; const router: Router = Router(); diff --git a/routes/settings/personalAccessTokens.ts b/routes/settings/personalAccessTokens.ts index 9aafe0077..303345a1a 100644 --- a/routes/settings/personalAccessTokens.ts +++ b/routes/settings/personalAccessTokens.ts @@ -3,14 +3,18 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; const router: Router = Router(); -import { getProviders } from '../../transitional'; -import { PersonalAccessToken } from '../../entities/token/token'; +import { getProviders } from '../../lib/transitional'; +import { PersonalAccessToken } from '../../business/entities/token/token'; import { ReposAppRequest } from '../../interfaces'; +type ResponseWithNewKey = Response & { + newKey: string; +}; + interface IPersonalAccessTokenForDisplay { active: boolean; expired: boolean; @@ -48,7 +52,7 @@ function translateTableToEntities( }); } -function getPersonalAccessTokens(req: ReposAppRequest, res, next) { +function getPersonalAccessTokens(req: ReposAppRequest, res: Response, next: NextFunction) { const providers = getProviders(req); const tokenProvider = providers.tokenProvider; const corporateId = req.individualContext.corporateIdentity.id; @@ -80,7 +84,7 @@ router.use(getPersonalAccessTokens); router.get('/', view); -function createToken(req: ReposAppRequest, res, next) { +function createToken(req: ReposAppRequest, res: ResponseWithNewKey, next: NextFunction) { const providers = getProviders(req); const tokenProvider = providers.tokenProvider; const insights = req.insights; @@ -136,7 +140,7 @@ router.post('/extension', createToken); router.post( '/delete', - asyncHandler(async (req: IRequestForSettingsPersonalAccessTokens, res, next) => { + asyncHandler(async (req: IRequestForSettingsPersonalAccessTokens, res: Response, next: NextFunction) => { const providers = getProviders(req); const tokenProvider = providers.tokenProvider; const revokeAll = req.body.revokeAll === '1'; diff --git a/routes/teams.ts b/routes/teams.ts index c1220135a..edf5c45ca 100644 --- a/routes/teams.ts +++ b/routes/teams.ts @@ -3,7 +3,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; const router: Router = Router(); import lowercaser from '../middleware/lowercaser'; @@ -11,7 +11,7 @@ import { ReposAppRequest } from '../interfaces'; import RouteTeamsPager from './teamsPager'; -router.use(function (req: ReposAppRequest, res, next) { +router.use(function (req: ReposAppRequest, res: Response, next: NextFunction) { req.individualContext.webContext.pushBreadcrumb('Teams'); req.reposContext = { section: 'teams', diff --git a/routes/teamsPager.ts b/routes/teamsPager.ts index 8dc93f1ab..d4225c2c6 100644 --- a/routes/teamsPager.ts +++ b/routes/teamsPager.ts @@ -7,7 +7,7 @@ import _ from 'lodash'; import asyncHandler from 'express-async-handler'; import { NextFunction, Response } from 'express'; -import { getProviders } from '../transitional'; +import { getProviders } from '../lib/transitional'; import { Operations } from '../business'; import { Team } from '../business'; import { UserContext } from '../business/user/aggregate'; diff --git a/routes/thanks.ts b/routes/thanks.ts index 04d7df55a..ff95bcfe6 100644 --- a/routes/thanks.ts +++ b/routes/thanks.ts @@ -5,7 +5,7 @@ import { ReposAppRequest } from '../interfaces'; import thisPackage from '../package.json'; -import { getProviders } from '../transitional'; +import { getProviders } from '../lib/transitional'; const express = require('express'); const router = express.Router(); diff --git a/routes/undo.ts b/routes/undo.ts index f0e61823e..a08e527ff 100644 --- a/routes/undo.ts +++ b/routes/undo.ts @@ -3,15 +3,15 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; const router: Router = Router(); import { Operations, Repository } from '../business'; -import { ErrorHelper, getProviders } from '../transitional'; -import { AuditLogRecord } from '../entities/auditLogRecord/auditLogRecord'; -import { daysInMilliseconds } from '../utils'; -import { AuditEvents } from '../entities/auditLogRecord'; +import { ErrorHelper, getProviders } from '../lib/transitional'; +import { AuditLogRecord } from '../business/entities/auditLogRecord/auditLogRecord'; +import { daysInMilliseconds } from '../lib/utils'; +import { AuditEvents } from '../business/entities/auditLogRecord'; import { IGitHubIdentity, IndividualContext } from '../business/user'; import { IMail } from '../lib/mailProvider'; import { GitHubRepositoryPermission, ReposAppRequest, UserAlertType } from '../interfaces'; @@ -291,7 +291,7 @@ async function undoTeamAdminRepoPermissionAsync( } router.use( - asyncHandler(async function (req: IHaveUndoCandidates, res, next) { + asyncHandler(async function (req: IHaveUndoCandidates, res: Response, next: NextFunction) { const { operations } = getProviders(req); if (!operations.allowUndoSystem) { res.status(404); @@ -322,7 +322,7 @@ router.use( router.post( '/', - asyncHandler(async (req: IHaveUndoCandidates, res, next) => { + asyncHandler(async (req: IHaveUndoCandidates, res: Response, next: NextFunction) => { const { operations } = getProviders(req); const insights = operations.insights; const link = req.individualContext.link; @@ -394,7 +394,7 @@ router.post( router.get( '/', - asyncHandler(async (req: IHaveUndoCandidates, res, next) => { + asyncHandler(async (req: IHaveUndoCandidates, res: Response, next: NextFunction) => { const { operations } = getProviders(req); const insights = operations.insights; insights?.trackMetric({ name: 'UndoPageViews', value: 1 }); diff --git a/routes/unlink.ts b/routes/unlink.ts index af5a7e0a6..3e79266a6 100644 --- a/routes/unlink.ts +++ b/routes/unlink.ts @@ -3,18 +3,18 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -import { Router } from 'express'; +import { NextFunction, Response, Router } from 'express'; import asyncHandler from 'express-async-handler'; const router: Router = Router(); -import { getProviders } from '../transitional'; -import { wrapError } from '../utils'; +import { getProviders } from '../lib/transitional'; +import { wrapError } from '../lib/utils'; import { IndividualContext } from '../business/user'; import { jsonError } from '../middleware'; import { ReposAppRequest, OrganizationMembershipState, UnlinkPurpose } from '../interfaces'; router.use( - asyncHandler(async function (req: ReposAppRequest, res, next) { + asyncHandler(async function (req: ReposAppRequest, res: Response, next: NextFunction) { const memberOfOrganizations = []; const { operations } = getProviders(req); const ghi = req.individualContext.getGitHubIdentity(); @@ -44,7 +44,7 @@ router.use( router.get( '/', - asyncHandler(async (req: ReposAppRequest, res, next) => { + asyncHandler(async (req: ReposAppRequest, res: Response, next: NextFunction) => { const link = req.individualContext.link; const id = req.individualContext.getGitHubIdentity().id; const { operations } = getProviders(req); @@ -110,7 +110,7 @@ export async function unlinkInteractive( router.post( '/', - asyncHandler(async function (req: ReposAppRequest, res, next) { + asyncHandler(async function (req: ReposAppRequest, res: Response, next: NextFunction) { const individualContext = req.individualContext; // TODO: validate return unlinkInteractive(false, individualContext, req, res, next); diff --git a/scripts/configuration.ts b/scripts/configuration.ts index 28f80acdc..8225c5164 100644 --- a/scripts/configuration.ts +++ b/scripts/configuration.ts @@ -3,24 +3,16 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -async function go(providers: IProviders): Promise { - const { config } = providers; - for (const key of Object.getOwnPropertyNames(config)) { - console.log(`${key}\n`); - console.dir(config[key]); - console.log(); - } -} +import job from '../job'; -import app from '../app'; -import { IProviders, IReposJob } from '../interfaces'; - -app.runJob( - async function ({ providers }: IReposJob) { - await go(providers); - return {}; +job.run( + async (providers) => { + const { config } = providers; + for (const key of Object.getOwnPropertyNames(config)) { + console.log(`${key}\n`); + console.dir(config[key]); + console.log(); + } }, - { - enableAllGitHubApps: true, - } + { name: 'Script: View configuration' } ); diff --git a/scripts/localEnvironment.ts b/scripts/localEnvironment.ts index a3fad71cd..8611726c5 100644 --- a/scripts/localEnvironment.ts +++ b/scripts/localEnvironment.ts @@ -3,25 +3,13 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // -// The local environment script is designed to allow for local debugging, test and -// development scenarios. The go method is called with resolved configuration. +import job from '../job'; -async function go(providers: IProviders): Promise { - // --------------------------------------------------------------------------- -} +// This script allows for quick inner-loop development. -// ----------------------------------------------------------------------------- -// Local script initialization -// ----------------------------------------------------------------------------- -import app from '../app'; -import { IProviders, IReposJob } from '../interfaces'; - -app.runJob( - async function ({ providers }: IReposJob) { - await go(providers); - return {}; +job.run( + async (providers) => { + // }, - { - enableAllGitHubApps: true, - } + { name: 'Local Environment' } ); diff --git a/scripts/migrateLinks/task.ts b/scripts/migrateLinks.ts similarity index 84% rename from scripts/migrateLinks/task.ts rename to scripts/migrateLinks.ts index f7aaa3b02..316a702af 100644 --- a/scripts/migrateLinks/task.ts +++ b/scripts/migrateLinks.ts @@ -14,14 +14,17 @@ // LINK_MIGRATION_OVERWRITE values : 'overwrite', 'skip' import throat from 'throat'; -import { IReposJob, ICorporateLink } from '../../interfaces'; -import { createAndInitializeLinkProviderInstance, ILinkProvider } from '../../lib/linkProviders'; -import { ErrorHelper } from '../../transitional'; +import job from '../job'; +import { ICorporateLink, IProviders } from '../interfaces'; +import { createAndInitializeLinkProviderInstance } from '../lib/linkProviders'; +import { getThirdPartyLinkById } from '../lib/transitional'; const parallelWorkLimit = 5; -export default async function migration({ providers }: IReposJob): Promise { +job.run(migration, { name: 'Link migration' }); + +async function migration(providers: IProviders): Promise { // const sourceLinkProvider = providers.linkProvider; const config = providers.config; const sourceLinkProviderName = 'table'; @@ -57,7 +60,7 @@ export default async function migration({ providers }: IReposJob): Promise await Promise.all( allSourceLinks.map((sourceLink: ICorporateLink) => throttle(async () => { - const existingLink = await getThirdPartyLink(destinationLinkProvider, sourceLink.thirdPartyId); + const existingLink = await getThirdPartyLinkById(destinationLinkProvider, sourceLink.thirdPartyId); if (existingLink && overwriteDestinationLinks) { console.warn('Removing existing destination link...'); await destinationLinkProvider.deleteLink(existingLink); @@ -101,17 +104,6 @@ export default async function migration({ providers }: IReposJob): Promise console.log(); } -async function getThirdPartyLink(linkProvider: ILinkProvider, thirdPartyId: string): Promise { - try { - return await linkProvider.getByThirdPartyId(thirdPartyId); - } catch (error) { - if (ErrorHelper.IsNotFound(error)) { - return null; - } - throw error; - } -} - async function getUserIdByUpn(graphProvider, upn: string): Promise { return new Promise((resolve, reject) => { graphProvider.getUserById(upn, (err, info) => { diff --git a/scripts/postgres/setup.ts b/scripts/postgres/setup.ts index 7bcf5fe3f..7adb4968c 100644 --- a/scripts/postgres/setup.ts +++ b/scripts/postgres/setup.ts @@ -12,7 +12,7 @@ import path from 'path'; initialize(); async function setup() { - const sql = fs.readFileSync(path.join(__dirname, '../../../pg.sql'), 'utf8'); + const sql = fs.readFileSync(path.join(__dirname, '../../data/pg.sql'), 'utf8'); const server = await prompt('postgres server: '); const adminUsername = await prompt('admin user: '); diff --git a/jest.config.ts b/test/jest.config.ts similarity index 66% rename from jest.config.ts rename to test/jest.config.ts index d71b5f019..ec96f57aa 100644 --- a/jest.config.ts +++ b/test/jest.config.ts @@ -1,3 +1,8 @@ +// +// Copyright (c) Microsoft. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + // eslint-disable-next-line n/no-unpublished-import import type { Config } from 'jest'; diff --git a/tsconfig.json b/tsconfig.json index 7edc1ea63..b04ed2bd9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,11 +1,12 @@ { "compilerOptions": { - "module": "commonjs", + "module": "NodeNext", "sourceMap": true, "outDir": "./dist", "incremental": true, "allowJs": true, - "moduleResolution": "node", + "declaration": true, + "moduleResolution": "nodenext", "resolveJsonModule": true, "esModuleInterop": true, "skipLibCheck": true, @@ -22,8 +23,8 @@ "./business/**/*", "./config/**/*", "./data/**/*", - "./entities/**/*", - "./features/**/*", + "business/entities/**/*", + "business/features/**/*", "./interfaces/**/*", "./jobs/**/*", "./lib/**/*", @@ -32,6 +33,6 @@ "./routes/**/*", "./scripts/**/*", "./test/**/*", - "./webhooks/**/*" + "business/webhooks/**/*" ] } diff --git a/views/contributions/eligible.pug b/views/contributions/eligible.pug index e1dd04859..3cad0a75f 100644 --- a/views/contributions/eligible.pug +++ b/views/contributions/eligible.pug @@ -6,10 +6,8 @@ extends ../layout block content - - var octicon = viewServices.octicon - .container - h5 CONFIDENTIAL - ADMINISTRATOR ACCESS ONLY + h5 ADMINISTRATOR ACCESS ONLY p Currently eligible corporate users to vote in the contribution system are listed below. p diff --git a/views/contributions/popular.pug b/views/contributions/popular.pug index 2ccd9f7c6..dacadaad8 100644 --- a/views/contributions/popular.pug +++ b/views/contributions/popular.pug @@ -6,8 +6,6 @@ extends ../layout block content - - var octicon = viewServices.octicon - .container p Popular repos people contribute to. diff --git a/views/contributions/voting/elections.pug b/views/contributions/voting/elections.pug index 0021a5886..5bff6d1fa 100644 --- a/views/contributions/voting/elections.pug +++ b/views/contributions/voting/elections.pug @@ -5,22 +5,32 @@ extends ../../layout -block content - - var fileSize = viewServices.fileSize - - var moment = viewServices.moment - - var octicon = viewServices.octicon - - //- var startMonthName = moment(start).format('MMMM') - //- var previousMonthName = moment(start).subtract(1, 'months').format('MMMM') +//- @cspell: ignore microsoftfossfund +block content .container - h2 Active Elections + h2 FOSS Fund Rounds + p. + The FOSS Fund is a way for Microsoft employees who contribute to open source + to support open source projects that they use and love. Public information + about this fund is available to be shared at #[a(href='https://aka.ms/microsoftfossfund') aka.ms/microsoftfossfund]. The + fund is distributed to projects based on the votes of Microsoft employees. if activeElections && activeElections.length + - var election1 = activeElections[0] + h3 Most recent funding round + h4: strong: a(href='/contributions/voting/' + election1.slug)= election1.title + p= election1.description + + h3 Past rounds each election in activeElections - //-pre= JSON.stringify(election, undefined, 2) - h2: a(href='/contributions/voting/' + election.slug)= election.title - p= election.description + if election.slug == election1.slug + //- nothing + else + //-pre= JSON.stringify(election, undefined, 2) + p + a(href='/contributions/voting/' + election.slug)= election.title + br + = election.description else - p No active voting opportunities. - + p No voting opportunities. diff --git a/views/contributions/voting/vote.pug b/views/contributions/voting/vote.pug index 1049fa3cc..546359c0d 100644 --- a/views/contributions/voting/vote.pug +++ b/views/contributions/voting/vote.pug @@ -24,9 +24,7 @@ mixin showNominee(election, nominee, iitemp, stripJsSingleQuote) p: small: em= nominee.justification block content - - var fileSize = viewServices.fileSize - var moment = viewServices.moment - - var octicon = viewServices.octicon - var stripJsSingleQuote = viewServices.stripJsSingleQuote //- var startMonthName = moment(start).format('MMMM') diff --git a/views/email/fossfund-vote.pug b/views/email/fossfund-vote.pug index 25b0f8987..94f43ceaf 100644 --- a/views/email/fossfund-vote.pug +++ b/views/email/fossfund-vote.pug @@ -48,16 +48,16 @@ block content p= election.description if userSettings && userSettings.contributionShareOptIn === true - //-h2 Launching soon, featuring you: the new opensource.microsoft.com + //-h2 Featuring you: the opensource.microsoft.com //-p Thanks for opting in to public data sharing. Your GitHub avatar and/or public contributions on GitHub may be featured. We will let you know when the new site launches. //-p: a(href='https://aka.ms/opensource/publicdatasharing') More info at aka.ms/opensource/publicdatasharing else if userSettings && userSettings.contributionShareOptIn === false - //-h2 The new opensource.microsoft.com site launches this month featuring many of your coworkers + //-h2 opensource.microsoft.com site features many of your coworkers //-p You have opted out of participating in the new site at this time. If you change your mind... //-p: a(href='https://aka.ms/opensource/publicdatasharing') More info and ability to opt-in at aka.ms/opensource/publicdatasharing else - h2 Want to be featured on the new opensource.microsoft.com site? - p Launched earlier this year, the new site features Microsoft employees who contribute to open source, whether part of their job, a hobby, or just something they do. + h2 Want to be featured on the opensource.microsoft.com site? + p The site features Microsoft employees who contribute to open source, whether part of their job, a hobby, or just something they do. p If you'd like to have your GitHub avatar appear on the homepage at times, and your contributions to other projects highlighted when they're made as part of the new "ecosystem" page, you can opt-in to sharing that already-public info. p: a(href='https://aka.ms/opensource/publicdatasharing') More info and ability to opt-in at aka.ms/opensource/publicdatasharing @@ -79,19 +79,20 @@ block content p We are trying to drive a change across Microsoft. We'd like to see more Microsofties contributing to open source communities, whether as part of your role, a Day of Learning project, updating project dependencies, hobbies, or pursuing off-hours projects and interests. h2 About the FOSS Fund - p Microsoft's FOSS Fund gives $10,000 to the most-voted-for open source software project. Projects are nominated and selected by those at Microsoft who contribute to communities that are not primarily Microsoft communities. + p Microsoft's FOSS Fund gives up to $10,000 to the most-voted-for open source software projects. Projects are nominated and selected by those at Microsoft who contribute to communities that are not primarily Microsoft communities. p Public information about the fund can be found at #[a(href='https://aka.ms/microsoftfossfund') https://aka.ms/microsoftfossfund], including previous fund winners. h3 Nominated projects p Projects must: ul - li Be used by Microsoft - li Have an OSI-approved license such as MIT, Apache 2.0, GPL, etc. + li Be used by Microsoft (used in Microsoft products and services) + li Have an #[a(href='https://opensource.org/licenses', target='_new') OSI-approved license] such as MIT, Apache 2.0, GPL, etc. li Must have a mechanism for receiving funds (the GitHub Sponsors team may be able to help) li Cannot be a Microsoft employee-led project + li Have a public-facing details of #[a(href='https://aka.ms/fossfundform/#funding', target='_new') who and how project sponsorship is distributed] h3 How to nominate a project for the next fund - p New FOSS Fund rounds are announced monthly via the #[a(href='https://idwebelements/GroupManagement.aspx?Group=osblast&Operation=join') osblast] discussion list. + p Nominate at any time using this #[a(href='https://aka.ms/fossfundform') form]. h3 Selection eligibility p Eligibility requirements: diff --git a/views/includes/corporateRepoMetadata.pug b/views/includes/corporateRepoMetadata.pug index 1ecb0cbf3..1b9db91d6 100644 --- a/views/includes/corporateRepoMetadata.pug +++ b/views/includes/corporateRepoMetadata.pug @@ -7,7 +7,6 @@ //- Optional inputs: createdUserLink //- Services -- var octicon = viewServices ? viewServices.octicon : null - var moment = viewServices ? viewServices.moment : null //- isBootstrap is currently being used to differentiate this include for whether it is diff --git a/views/message.pug b/views/message.pug index 10feac70c..d04cb6a70 100644 --- a/views/message.pug +++ b/views/message.pug @@ -6,6 +6,9 @@ extends layout block content + if clearLocalStorage + script. + localStorage.clear(); div.container#top(style='margin-top:60px') div.container#content div.row diff --git a/views/nav.pug b/views/nav.pug index 97ce212a6..0b13159d2 100644 --- a/views/nav.pug +++ b/views/nav.pug @@ -51,14 +51,14 @@ mixin userAzure div.navbar.navbar-default.second-row-nav div.container div.navbar-header - //-button.navbar-toggle.collapsed(type='button', data-toggle='collapse', data-target='.nav-collapse') - //-button.navbar-toggle(type='button', data-toggle='collapse', data-target='.nav-collapse') + button.navbar-toggle.collapsed(type='button', data-toggle='collapse', data-target='.nav-collapse') + button.navbar-toggle(type='button', data-toggle='collapse', data-target='.nav-collapse') span.sr-only Toggle navigation span.icon-bar span.icon-bar span.icon-bar - //- a.navbar-brand(href='./')= page.folderMetadata.title - //nav.collapse.navbar-collapse.nav-collapse(role='navigation') + // a.navbar-brand(href='./')= page.folderMetadata.title + nav.collapse.navbar-collapse.nav-collapse(role='navigation') nav(role='navigation') div.container(style='margin-top:24px;margin-bottom:12px') div.row(style=(user && !error && ossLink) ? 'margin-left:0' : 'margin-left:-30px') diff --git a/views/org/pending.pug b/views/org/pending.pug index 0e29de88a..348bbcaa9 100644 --- a/views/org/pending.pug +++ b/views/org/pending.pug @@ -78,20 +78,7 @@ block content div.row div.col-md-8.col-lg-8 h1 Want to join #{organization.name}? - if organization && organization.pilot_program && organization.pilot_program === 'pilot' - //- Pilot program - //- Due to SAML, auto-invite and accept does not work - p.lead 1ES Engineering Pilot - ul - li An invitation will be sent to your GitHub account. - li You accept the invitation in on GitHub.com. Due to SAML (single Microsoft sign-on), you may need to authenticate again with Microsoft AAD. - li You come back to this site and continue onboarding. - p   - form(method='post') - p - input.btn.btn-primary.btn-lg(type='submit', value='Join ' + organization.name) - - else if hasIncreasedScope && supportsExpressJoinExperience + if hasIncreasedScope && supportsExpressJoinExperience form(method='post') p(style='margin-top:24px') input.btn.btn-primary.btn-huge(type='submit', value='Join ' + organization.name) diff --git a/views/org/team/index.pug b/views/org/team/index.pug index f807d7042..503250909 100644 --- a/views/org/team/index.pug +++ b/views/org/team/index.pug @@ -87,13 +87,6 @@ mixin membersList(typeOfList, membersList, isAdmin, moreMembersToShow) p.lead.text-primary.text-center: small … and others block content - - //- Services - - var languageColor = viewServices.languageColor - - var octicon = viewServices.octicon - - var fileSize = viewServices.fileSize - - var moment = viewServices.moment - //- Variables - var maximumMembersToShow = (4 * 3) - 1 - var maximumRepositoriesToShow = 5 @@ -101,6 +94,7 @@ block content //- View services - var languageColor = viewServices.languageColor - var octicon = viewServices.octicon + - var moment = viewServices.moment //- Mode variables - var admin = teamPermissions.allowAdministration @@ -343,9 +337,12 @@ block content .repo(id=repo.name, style='padding-bottom:36px;display:block') h3 a(href='/' + localOrgName + '/repos/' + repo.name)= repo.name - if repo.private === true + if repo.visibility + |   + .label.label-warning(class={shrink66: !hugeHeading, shrink50: hugeHeading}) #{repo.visibility} + if repo.has_pages |   - .label.label-warning.shrink66(title='This is a private repository and not open source.') Private + .label.label-warning(class={shrink66: !hugeHeading, shrink50: hugeHeading}) pages if repoPermissions && specificTeam = ' ' if repoPermissions.admin @@ -361,7 +358,7 @@ block content if repo.language li span(style={color: languageColor(repo.language)}) - != octicon('primitive-dot', 10) + != octicon('dot-fill', 10) = ' ' + repo.language if repo.stargazers_count li diff --git a/views/organization/whois/result.pug b/views/organization/whois/result.pug index adde2067f..4a044bab3 100644 --- a/views/organization/whois/result.pug +++ b/views/organization/whois/result.pug @@ -173,6 +173,12 @@ block content p.lead DANGER ZONE - NO CONFIRMATION p input.btn.btn-danger(type='submit', value='Remove link', name='remove-link-only') + if query && query.collaboratorRepositories && query.collaboratorRepositories.length + p + input.btn.btn-danger(type='submit', value='Remove collaborator grants', name='remove-collaboration') + if query && query.collaboratorRepositories && query.collaboratorRepositories.length > 100 + p + input.btn.btn-danger(type='submit', value='Remove 100 collaborator grants', name='remove-collaboration-100') if query && query.collaboratorRepositories && query.collaboratorRepositories.length h2 Active individual Collaborator permissions on repos diff --git a/views/people/index.pug b/views/people/index.pug index 5266558b8..edaf41e3d 100644 --- a/views/people/index.pug +++ b/views/people/index.pug @@ -51,7 +51,7 @@ block content //-ul.nav.nav-pills li(class=(search.sort === 'Alphabet' ? 'active' : ''), title='Alphabetically sorted') a(href='?sort=Alphabet&tag=' + (tag ? tag : '') + (query.phrase ? '&q=' + query.phrase : '')) - != octicon('text-size', 20) + != octicon('typography', 20) | Name form.form-horizontal#entitySearch(style='margin-top:24px') diff --git a/views/repos/defaultBranch.pug b/views/repos/defaultBranch.pug deleted file mode 100644 index f05e585db..000000000 --- a/views/repos/defaultBranch.pug +++ /dev/null @@ -1,88 +0,0 @@ -//- -//- Copyright (c) Microsoft. -//- Licensed under the MIT license. See LICENSE file in the project root for full license information. -//- - -extends ../layout - -block content - - //- Services - - var languageColor = viewServices.languageColor - - var octicon = viewServices.octicon - - var fileSize = viewServices.fileSize - - //- Variables - - var githubUrl = 'https://github.com/' + repo.full_name - - var cloneUrl = repo.clone_url - - var sshUrl = repo.ssh_url - - var admin = repoPermissions && repoPermissions.allowAdministration - - .container - .row: .col-md-12 - if fromReposPage - .nav - ul.pager.zero-pad-bottom - li.previous - a(href='javascript:window.history.back()') - span(aria-hidden=true) ← - = ' Back' - - var hugeHeading = repo.name.length < 33 - h1(class={huge: hugeHeading}) - a(href='https://github.com/' + repo.full_name, target="_blank")= repo.name - if repo.private === true - |   - .label.label-warning(class={shrink66: !hugeHeading, shrink50: hugeHeading}) Private - h6= repo.full_name.replace('/' + repo.name, '') + ' organization' - if repo.description - p.lead=repo.description - - include ./pills - - if admin - if !organizationSupportsUpdatesApp - h4 Default branch rename #[span.label.label-muted PREVIEW] - ul.list-inline.list-horizontal-space - li - | Current default branch name - br - strong= repo.default_branch - p. - The #{organization.name} is not configured to allow renames at this time. - else - //- repo.default_branch !== 'main' - h4 Default branch rename #[span.label.label-muted PREVIEW] - ul.list-inline.list-horizontal-space - li - | Current default branch name - br - strong= repo.default_branch - p You have administrative rights to this repo and can choose to rename the default branch. - if repo.default_branch == 'main' - p: strong You're already using the default branch "main", no action required. - p. - Easily convert the default branch of this project. Note that there are - many potential side effects, including impacting deep URL links, continuous - integration and deployment systems, and likely this will require cleanup - work. - p. - This automated default branch rename capability: - ul - li Creates a new branch based off of the current default branch's latest commit - li Reassigns any protected branch settings to the new default branch from the former - li Updates any open pull requests against the current default branch for the new branch - li Updates the default branch to the new branch - li Deletes the current default branch - p The automated operation will halt if a major error is detected, but will not revert the changes completely. It will take 20-120 seconds to process. Please anticipate updating this repo to take some time, and resource for this change. This process may timeout if there are a large number of open pull requests. - - form(method='post', action=repository.baseUrl + 'defaultBranch') - p: strong New default branch name - input.form-control(name='targetBranchName', type='text', placeholder='The new default branch name to use', value='main') - br - input.btn.btn-sm( - type='submit', - name='rename-default-branch' - class='btn-danger', - value='Rename default branch', - onclick='return confirm(\'Are you sure that you want to rename the default branch? Additional work may be required to address any errors or configure systems such as webhooks, continuous integration, etc.\');' - title='Select this operation to begin the default branch rename process automatically') diff --git a/views/repos/delete.pug b/views/repos/delete.pug index f8886d855..57f420726 100644 --- a/views/repos/delete.pug +++ b/views/repos/delete.pug @@ -7,11 +7,6 @@ extends ../layout block content - //- Services - - var languageColor = viewServices.languageColor - - var octicon = viewServices.octicon - - var fileSize = viewServices.fileSize - //- View constants - var maxReaders = 10 diff --git a/views/repos/history.pug b/views/repos/history.pug index 2c36be2b5..d7100cbf6 100644 --- a/views/repos/history.pug +++ b/views/repos/history.pug @@ -8,9 +8,6 @@ extends ../layout block content //- Services - - var languageColor = viewServices.languageColor - - var octicon = viewServices.octicon - - var fileSize = viewServices.fileSize - var moment = viewServices.moment //- Variables diff --git a/views/repos/index.pug b/views/repos/index.pug index 8ffce2683..2c657276f 100644 --- a/views/repos/index.pug +++ b/views/repos/index.pug @@ -81,7 +81,7 @@ block content li(class=(search.sort === 'Alphabet' ? 'active' : ''), title='Sort by repo name') a(href='?sort=Alphabet&tag=' + (specificTeamId ? '&teamRepos=' + specificTeamId : '') + (tag ? tag : '') + (query.tt ? '&tt=' + query.tt : '') + (query.phrase ? '&q=' + query.phrase : '')) //- i.glyphicon.glyphicon-sort-by-alphabet - != octicon('text-size', 20) + != octicon('typography', 20) | Name //-i.glyphicon.glyphicon-triangle-bottom li(class=(search.sort === 'Updated' ? 'active' : '')) @@ -225,9 +225,12 @@ block content if showIds = repo.id + ' ' a(href='/' + localOrgName + '/repos/' + repo.name)= repo.name - if repo.private === true + if repo.visibility |   - .label.label-warning.shrink66(title='This is a private repository and not open source.') Private + .label.label-warning(class={shrink66: !hugeHeading, shrink50: hugeHeading}) #{repo.visibility} + if repo.has_pages + |   + .label.label-warning(class={shrink66: !hugeHeading, shrink50: hugeHeading}) pages if repo.permissions && specificTeam = ' ' if repo.permissions.admin @@ -248,7 +251,7 @@ block content if repo.language li span(style={color: languageColor(repo.language)}) - != octicon('primitive-dot', 10) + != octicon('dot-fill', 10) = ' ' + repo.language if repo.stargazers_count li diff --git a/views/repos/permissions.pug b/views/repos/permissions.pug index 1cbc9e9de..5168fc4b3 100644 --- a/views/repos/permissions.pug +++ b/views/repos/permissions.pug @@ -66,9 +66,7 @@ mixin teamsList(teams, hideJoinOption) block content //- Services - - var languageColor = viewServices.languageColor - var octicon = viewServices.octicon - - var fileSize = viewServices.fileSize //- View constants - var maxReaders = 10 diff --git a/views/repos/pills.pug b/views/repos/pills.pug index 7800be760..9f1e89b86 100644 --- a/views/repos/pills.pug +++ b/views/repos/pills.pug @@ -6,6 +6,5 @@ .row: .col-md-12(style='margin-bottom: 32px; margin-top: 16px') ul.nav.nav-tabs li(role='presentation', class={active: !reposSubView || reposSubView === 'default'}): a(href=repository.baseUrl) Overview - li(role='presentation', class={active: reposSubView === 'defaultBranch'}): a(href=repository.baseUrl + 'defaultBranch/') Default Branch Name li(role='presentation', class={active: reposSubView === 'permissions'}): a(href=repository.baseUrl + 'permissions/') Permissions li(role='presentation', class={active: reposSubView === 'history'}): a(href=repository.baseUrl + 'history/') History diff --git a/views/repos/repo.pug b/views/repos/repo.pug index da2e35ae1..fd3d575a7 100644 --- a/views/repos/repo.pug +++ b/views/repos/repo.pug @@ -13,27 +13,6 @@ mixin simplePersonView(account) div(style='display:block;vertical-align:middle') h4 a(href='/people?q=' + account.login)= shorthandName - //-ul.list-inline - if shorthandName && shorthandName !== account.login - li - span(title=account.login + ' is the GitHub username for ' + shorthandName)= account.login - if corporateIdentity - if link && !corporate - li.text-warning!= octicon('link', 16) - li - span(title=link.aadoid)= link.aadupn - else - li!= octicon('link', 16) - li - span(title=corporateIdentity + ' is the corporate identity for ' + shorthandName)= corporateIdentity - //- just corporate e-mails here, not user emails - if email - li - a(href='mailto:' + email, title='Send corporate email to ' + email) - != octicon('mail', 16) - if serviceAccount - li!= octicon('hubot', 16) - li Service account mixin teamsList(teams, hideJoinOption) if teams && teams.length > 0 @@ -96,9 +75,12 @@ block content - var hugeHeading = repo.name.length < 33 h1(class={huge: hugeHeading}) a(href='https://github.com/' + repo.full_name, target="_blank")= repo.name - if repo.private === true + if repo.visibility + |   + .label.label-warning(class={shrink66: !hugeHeading, shrink50: hugeHeading}) #{repo.visibility} + if repo.has_pages |   - .label.label-warning(class={shrink66: !hugeHeading, shrink50: hugeHeading}) Private + .label.label-warning(class={shrink66: !hugeHeading, shrink50: hugeHeading}) pages h6= repo.full_name.replace('/' + repo.name, '') + ' organization' if repo.fork h6 @@ -123,7 +105,7 @@ block content if repo.language li span(style={color: languageColor(repo.language)}) - != octicon('primitive-dot', 10) + != octicon('dot-fill', 10) = ' ' + repo.language if repo.license && repo.license.featured && repo.license.name li @@ -185,9 +167,6 @@ block content | Default branch br strong= repo.default_branch - if repo.default_branch === 'master' && admin - span   - a.btn.btn-sm.btn-muted(href=repository.baseUrl + 'defaultBranch') Rename... if organizationSupportsUpdatesApp && repo.default_branch !== 'main' && admin .alert.alert-gray diff --git a/views/teams/index.pug b/views/teams/index.pug index 4f9b585a7..c1c3b23f5 100644 --- a/views/teams/index.pug +++ b/views/teams/index.pug @@ -9,7 +9,6 @@ block append js_doc_ready include ../js/search.js block content - - var languageColor = viewServices.languageColor - var octicon = viewServices.octicon .container From ff4576c796c434bf16bfa30a7fe5bc2aaecaea49 Mon Sep 17 00:00:00 2001 From: furniturewalatkNIH <137434977+furniturewalatkNIH@users.noreply.github.com> Date: Thu, 1 Aug 2024 11:28:49 -0500 Subject: [PATCH 2/3] Update nav.pug - Joels change done to fix the issue seen on merge --- views/nav.pug | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/nav.pug b/views/nav.pug index 0b13159d2..7e7c068e2 100644 --- a/views/nav.pug +++ b/views/nav.pug @@ -57,7 +57,7 @@ div.navbar.navbar-default.second-row-nav span.icon-bar span.icon-bar span.icon-bar - // a.navbar-brand(href='./')= page.folderMetadata.title + a.navbar-brand(href='./')= page.folderMetadata.title nav.collapse.navbar-collapse.nav-collapse(role='navigation') nav(role='navigation') div.container(style='margin-top:24px;margin-bottom:12px') From 51e51c1fe8898af86b653965ff11871903366665 Mon Sep 17 00:00:00 2001 From: furniturewalatkNIH <137434977+furniturewalatkNIH@users.noreply.github.com> Date: Thu, 1 Aug 2024 16:22:32 -0500 Subject: [PATCH 3/3] commenting out line 60 --- views/nav.pug | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/nav.pug b/views/nav.pug index 7e7c068e2..67d2ef894 100644 --- a/views/nav.pug +++ b/views/nav.pug @@ -57,7 +57,7 @@ div.navbar.navbar-default.second-row-nav span.icon-bar span.icon-bar span.icon-bar - a.navbar-brand(href='./')= page.folderMetadata.title + // a.navbar-brand(href='./')= page.folderMetadata.title nav.collapse.navbar-collapse.nav-collapse(role='navigation') nav(role='navigation') div.container(style='margin-top:24px;margin-bottom:12px')