From bc34b16c864a0fc11dbf250f82eb6b521b29f0b6 Mon Sep 17 00:00:00 2001 From: Isla Koenigsknecht Date: Thu, 4 Apr 2024 11:57:05 -0400 Subject: [PATCH 01/48] Fix: Not initializing on reconnection when general channel was deleted (#2334) (#2400) * Fix issue with users joining a community where the general channel was deleted while they were offline * Update e2e tests to include case for this bug * Update CHANGELOG.md --- CHANGELOG.md | 1 + .../src/nest/storage/storage.service.ts | 14 ++-- .../src/tests/multipleClients.test.ts | 84 +++++++++++++++++-- .../channelDeletionResponse.saga.ts | 2 +- .../channelsReplicated.saga.ts | 27 +++--- 5 files changed, 101 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ed1ba5b24..59c0741d0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ # Fixes * Allow JPEG and GIF files as profile photos ([#2332](https://github.com/TryQuiet/quiet/issues/2332)) +* Fixes issues with recreating general channel when deleted while offline ([#2334](https://github.com/TryQuiet/quiet/issues/2334)) # New features diff --git a/packages/backend/src/nest/storage/storage.service.ts b/packages/backend/src/nest/storage/storage.service.ts index 6ff620570d..54d7a65b0a 100644 --- a/packages/backend/src/nest/storage/storage.service.ts +++ b/packages/backend/src/nest/storage/storage.service.ts @@ -620,15 +620,15 @@ export class StorageService extends EventEmitter { } } await repo.db.load() - const allEntries = this.getAllEventLogRawEntries(repo.db) + // const allEntries = this.getAllEventLogRawEntries(repo.db) await repo.db.close() await repo.db.drop() - const hashes = allEntries.map(e => CID.parse(e.hash)) - const files = allEntries - .map(e => { - return e.payload.value.media - }) - .filter(isDefined) + // const hashes = allEntries.map(e => CID.parse(e.hash)) + // const files = allEntries + // .map(e => { + // return e.payload.value.media + // }) + // .filter(isDefined) // await this.deleteChannelFiles(files) // await this.deleteChannelMessages(hashes) this.publicChannelsRepos.delete(channelId) diff --git a/packages/e2e-tests/src/tests/multipleClients.test.ts b/packages/e2e-tests/src/tests/multipleClients.test.ts index 5ec4b301aa..8da0cade98 100644 --- a/packages/e2e-tests/src/tests/multipleClients.test.ts +++ b/packages/e2e-tests/src/tests/multipleClients.test.ts @@ -25,6 +25,8 @@ describe('Multiple Clients', () => { let secondChannelUser1: Channel + let thirdChannelOwner: Channel + let channelContextMenuOwner: ChannelContextMenu let invitationCode: string @@ -37,6 +39,7 @@ describe('Multiple Clients', () => { const communityName = 'testcommunity' const displayedCommunityName = 'Testcommunity' const newChannelName = 'mid-night-club' + const thirdChannelName = 'delete-this' const sleep = async (time = 1000) => { await new Promise(resolve => @@ -342,16 +345,42 @@ describe('Multiple Clients', () => { const channels = await sidebarOwner.getChannelList() expect(channels.length).toEqual(2) }) + + it('Channel deletion - Owner creates third channel', async () => { + await sidebarOwner.addNewChannel(thirdChannelName) + await sidebarOwner.switchChannel(thirdChannelName) + thirdChannelOwner = new Channel(users.owner.app.driver, thirdChannelName) + const messages = await thirdChannelOwner.getUserMessages(users.owner.username) + expect(messages.length).toEqual(1) + await new Promise(resolve => + setTimeout(() => { + resolve() + }, 2000) + ) + const channels = await sidebarUser1.getChannelList() + expect(channels.length).toEqual(3) + }) + // End of tests for Windows if (process.platform !== 'win32') { - it('Leave community', async () => { - console.log('TEST 2') - const settingsModal = await new Sidebar(users.user1.app.driver).openSettings() - const isSettingsModal = await settingsModal.element.isDisplayed() - expect(isSettingsModal).toBeTruthy() - await settingsModal.openLeaveCommunityModal() - await settingsModal.leaveCommunityButton() + it('User 1 closes app', async () => { + console.log('User 1 closes app') + await users.user1.app?.close() }) + + // Delete third channel while guest is absent + it('Channel deletion - Owner deletes third channel', async () => { + console.log('TEST 2.5') + await new Promise(resolve => setTimeout(() => resolve(), 10000)) + const isThirdChannel = await thirdChannelOwner.messageInput.isDisplayed() + expect(isThirdChannel).toBeTruthy() + await channelContextMenuOwner.openMenu() + await channelContextMenuOwner.openDeletionChannelModal() + await channelContextMenuOwner.deleteChannel() + const channels = await sidebarOwner.getChannelList() + expect(channels.length).toEqual(2) + }) + // Delete general channel while guest is absent it('Channel deletion - Owner recreates general channel', async () => { console.log('TEST 3') @@ -365,6 +394,47 @@ describe('Multiple Clients', () => { expect(channels.length).toEqual(2) }) + it('User 1 re-opens app', async () => { + console.log('User 1 re-opens app') + await users.user1.app?.open() + await new Promise(resolve => setTimeout(() => resolve(), 30000)) + }) + + // Check correct channels replication + it('Channel deletion - User sees information about recreation general channel and see correct amount of messages (#2334)', async () => { + console.log('TESTING - ISSUE 2334') + generalChannelUser1 = new Channel(users.user1.app.driver, 'general') + await generalChannelUser1.element.isDisplayed() + console.timeEnd(`[${users.user1.app.name}] '${users.user2.username}' joining community time`) + await new Promise(resolve => + setTimeout(() => { + resolve() + }, 10000) + ) + + await generalChannelUser1.waitForUserMessage( + users.owner.username, + `@${users.owner.username} deleted all messages in #general` + ) + await generalChannelUser1.waitForUserMessage( + users.owner.username, + `@${users.owner.username} deleted #${thirdChannelName}` + ) + await generalChannelUser1.waitForUserMessage( + users.owner.username, + `@${users.user2.username} has joined Testcommunity! 🎉` + ) + }) + + it('Leave community', async () => { + console.log('TEST 2') + const settingsModal = await new Sidebar(users.user1.app.driver).openSettings() + const isSettingsModal = await settingsModal.element.isDisplayed() + expect(isSettingsModal).toBeTruthy() + await settingsModal.openLeaveCommunityModal() + await settingsModal.leaveCommunityButton() + }) + it('Leave community - Guest re-join to community successfully', async () => { console.log('TEST 4') const debugModal = new DebugModeModal(users.user1.app.driver) diff --git a/packages/state-manager/src/sagas/publicChannels/channelDeletionResponse/channelDeletionResponse.saga.ts b/packages/state-manager/src/sagas/publicChannels/channelDeletionResponse/channelDeletionResponse.saga.ts index 9e53eab2b2..b0d90d0182 100644 --- a/packages/state-manager/src/sagas/publicChannels/channelDeletionResponse/channelDeletionResponse.saga.ts +++ b/packages/state-manager/src/sagas/publicChannels/channelDeletionResponse/channelDeletionResponse.saga.ts @@ -60,7 +60,7 @@ export function* channelDeletionResponseSaga( let newGeneralChannel: PublicChannelStorage | undefined = yield* select(publicChannelsSelectors.generalChannel) while (!newGeneralChannel) { log('General channel has not been replicated yet') - yield* delay(500) + yield* delay(1000) newGeneralChannel = yield* select(publicChannelsSelectors.generalChannel) } yield* put(publicChannelsActions.setCurrentChannel({ channelId: newGeneralChannel.id })) diff --git a/packages/state-manager/src/sagas/publicChannels/channelsReplicated/channelsReplicated.saga.ts b/packages/state-manager/src/sagas/publicChannels/channelsReplicated/channelsReplicated.saga.ts index 4b431b4fc6..ebf15f8f16 100644 --- a/packages/state-manager/src/sagas/publicChannels/channelsReplicated/channelsReplicated.saga.ts +++ b/packages/state-manager/src/sagas/publicChannels/channelsReplicated/channelsReplicated.saga.ts @@ -13,7 +13,8 @@ const log = logger('channels') export function* channelsReplicatedSaga( action: PayloadAction['payload']> ): Generator { - log('Syncing channels') + // TODO: Refactor to use QuietLogger + log(`Syncing channels: ${JSON.stringify(action.payload, null, 2)}`) const { channels } = action.payload const _locallyStoredChannels = yield* select(publicChannelsSelectors.publicChannels) const locallyStoredChannels = _locallyStoredChannels.map(channel => channel.id) @@ -24,20 +25,10 @@ export function* channelsReplicatedSaga( const databaseStoredChannelsIds = databaseStoredChannels.map(channel => channel.id) console.log({ locallyStoredChannels, databaseStoredChannelsIds }) - // Removing channels from store - if (databaseStoredChannelsIds.length > 0) { - for (const channelId of locallyStoredChannels) { - if (!databaseStoredChannelsIds.includes(channelId)) { - log(`Removing #${channelId} from store`) - yield* put(publicChannelsActions.deleteChannel({ channelId })) - yield* take(publicChannelsActions.completeChannelDeletion) - } - } - } - // Upserting channels to local storage for (const channel of databaseStoredChannels) { if (!locallyStoredChannels.includes(channel.id)) { + // TODO: Refactor to use QuietLogger log(`Adding #${channel.name} to store`) yield* put( publicChannelsActions.addChannel({ @@ -52,6 +43,18 @@ export function* channelsReplicatedSaga( } } + // Removing channels from store + if (databaseStoredChannelsIds.length > 0) { + for (const channelId of locallyStoredChannels) { + if (!databaseStoredChannelsIds.includes(channelId)) { + // TODO: Refactor to use QuietLogger + log(`Removing #${channelId} from store`) + yield* put(publicChannelsActions.deleteChannel({ channelId })) + yield* take(publicChannelsActions.completeChannelDeletion) + } + } + } + const currentChannelCache = yield* select(publicChannelsSelectors.currentChannelMessages) const currentChannelRepository = yield* select(messagesSelectors.currentPublicChannelMessagesEntries) From 0e587b1c6b40cfc6df7b9499020f5d801b9149ae Mon Sep 17 00:00:00 2001 From: Lucas Leblow Date: Thu, 4 Apr 2024 13:06:37 -0700 Subject: [PATCH 02/48] Publish - @quiet/desktop@2.2.0-alpha.0 - @quiet/mobile@2.2.0-alpha.0 --- package-lock.json | 6872 ++++----------------- packages/desktop/CHANGELOG.md | 34 + packages/desktop/package-lock.json | 4 +- packages/desktop/package.json | 2 +- packages/mobile/CHANGELOG.md | 32 + packages/mobile/android/app/build.gradle | 4 +- packages/mobile/ios/Quiet/Info.plist | 20 +- packages/mobile/ios/QuietTests/Info.plist | 4 +- packages/mobile/package-lock.json | 4 +- packages/mobile/package.json | 2 +- 10 files changed, 1204 insertions(+), 5774 deletions(-) diff --git a/package-lock.json b/package-lock.json index a7ee97b2e4..1545f1aaba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,39 +5,16 @@ "packages": { "": { "name": "root", - "dependencies": { - "@typescript-eslint/eslint-plugin": "6.7.4", - "@typescript-eslint/parser": "6.7.4", - "eslint": "^8.50.0", - "eslint-config-prettier": "^9.0.0", - "eslint-config-standard": "^17.1.0", - "eslint-config-standard-with-typescript": "^39.1.0", - "eslint-plugin-import": "^2.27.5", - "eslint-plugin-jest": "^27.4.2", - "eslint-plugin-jsx-a11y": "^6.7.1", - "eslint-plugin-node": "^11.1.0", - "eslint-plugin-prettier": "^5.0.0", - "eslint-plugin-promise": "^6.1.1", - "eslint-plugin-react-hooks": "^4.6.0", - "prettier": "^3.0.3" - }, "devDependencies": { "husky": "^9.0.11", - "lerna": "^6.6.2" + "lerna": "^6.6.2", + "typescript": "^4.9.3" }, "engines": { "node": "18.12.1", "npm": "8.19.2" } }, - "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==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/@babel/code-frame": { "version": "7.22.5", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz", @@ -135,116 +112,12 @@ "node": ">=4" } }, - "node_modules/@babel/runtime": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.0.tgz", - "integrity": "sha512-Chk32uHMg6TnQdvw2e9IlqPpFX/6NLuK0Ys2PqLb7/gL5uFn9mXvK715FGLlOLQrcO4qIkNHkvPGktzzXexsFw==", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "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==", - "dependencies": { - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", - "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "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" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "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==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@eslint/js": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", - "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, "node_modules/@gar/promisify": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", "dev": true }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.14", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", - "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", - "dependencies": { - "@humanwhocodes/object-schema": "^2.0.2", - "debug": "^4.3.1", - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", - "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==" - }, "node_modules/@hutson/parse-repository-url": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz", @@ -606,6 +479,7 @@ "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" @@ -618,6 +492,7 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, "engines": { "node": ">= 8" } @@ -626,6 +501,7 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" @@ -1472,17 +1348,6 @@ "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==", - "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/unts" - } - }, "node_modules/@sigstore/protobuf-specs": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.1.0.tgz", @@ -1567,16 +1432,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" - }, - "node_modules/@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==" - }, "node_modules/@types/minimatch": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz", @@ -1601,272 +1456,6 @@ "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", "dev": true }, - "node_modules/@types/semver": { - "version": "7.5.8", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", - "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==" - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.7.4.tgz", - "integrity": "sha512-DAbgDXwtX+pDkAHwiGhqP3zWUGpW49B7eqmgpPtg+BKJXwdct79ut9+ifqOFPJGClGKSHXn2PTBatCnldJRUoA==", - "dependencies": { - "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.7.4", - "@typescript-eslint/type-utils": "6.7.4", - "@typescript-eslint/utils": "6.7.4", - "@typescript-eslint/visitor-keys": "6.7.4", - "debug": "^4.3.4", - "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": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/eslint-plugin/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/@typescript-eslint/eslint-plugin/node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.7.4.tgz", - "integrity": "sha512-I5zVZFY+cw4IMZUeNCU7Sh2PO5O57F7Lr0uyhgCJmhN/BuTlnc55KxPonR4+EM3GBdfiCyGZye6DgMjtubQkmA==", - "dependencies": { - "@typescript-eslint/scope-manager": "6.7.4", - "@typescript-eslint/types": "6.7.4", - "@typescript-eslint/typescript-estree": "6.7.4", - "@typescript-eslint/visitor-keys": "6.7.4", - "debug": "^4.3.4" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.7.4.tgz", - "integrity": "sha512-SdGqSLUPTXAXi7c3Ob7peAGVnmMoGzZ361VswK2Mqf8UOYcODiYvs8rs5ILqEdfvX1lE7wEZbLyELCW+Yrql1A==", - "dependencies": { - "@typescript-eslint/types": "6.7.4", - "@typescript-eslint/visitor-keys": "6.7.4" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.7.4.tgz", - "integrity": "sha512-n+g3zi1QzpcAdHFP9KQF+rEFxMb2KxtnJGID3teA/nxKHOVi3ylKovaqEzGBbVY2pBttU6z85gp0D00ufLzViQ==", - "dependencies": { - "@typescript-eslint/typescript-estree": "6.7.4", - "@typescript-eslint/utils": "6.7.4", - "debug": "^4.3.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/types": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.7.4.tgz", - "integrity": "sha512-o9XWK2FLW6eSS/0r/tgjAGsYasLAnOWg7hvZ/dGYSSNjCh+49k5ocPN8OmG5aZcSJ8pclSOyVKP2x03Sj+RrCA==", - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.4.tgz", - "integrity": "sha512-ty8b5qHKatlNYd9vmpHooQz3Vki3gG+3PchmtsA4TgrZBKWHNjWfkQid7K7xQogBqqc7/BhGazxMD5vr6Ha+iQ==", - "dependencies": { - "@typescript-eslint/types": "6.7.4", - "@typescript-eslint/visitor-keys": "6.7.4", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/typescript-estree/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/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.7.4.tgz", - "integrity": "sha512-PRQAs+HUn85Qdk+khAxsVV+oULy3VkbH3hQ8hxLRJXWBEd7iI+GbQxH5SEUSH7kbEoTp6oT1bOwyga24ELALTA==", - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.7.4", - "@typescript-eslint/types": "6.7.4", - "@typescript-eslint/typescript-estree": "6.7.4", - "semver": "^7.5.4" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - } - }, - "node_modules/@typescript-eslint/utils/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/@typescript-eslint/utils/node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.4.tgz", - "integrity": "sha512-pOW37DUhlTZbvph50x5zZCkFn3xzwkGtNoJHzIM3svpiSkJzwOYr/kVBaXmf+RAQiUDs1AHEZVNPg6UJCJpwRA==", - "dependencies": { - "@typescript-eslint/types": "6.7.4", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "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==" - }, "node_modules/@yarnpkg/lockfile": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", @@ -1941,25 +1530,6 @@ "node": ">=6.5" } }, - "node_modules/acorn": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", - "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, "node_modules/add-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/add-stream/-/add-stream-1.0.0.tgz", @@ -2005,21 +1575,6 @@ "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==", - "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-colors": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", @@ -2048,6 +1603,7 @@ "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" } @@ -2056,6 +1612,7 @@ "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" }, @@ -2088,30 +1645,8 @@ "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, - "node_modules/aria-query": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", - "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", - "dependencies": { - "dequal": "^2.0.3" - } - }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", - "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", - "dependencies": { - "call-bind": "^1.0.5", - "is-array-buffer": "^3.0.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true }, "node_modules/array-differ": { "version": "3.0.0", @@ -2128,123 +1663,15 @@ "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==", "dev": true }, - "node_modules/array-includes": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz", - "integrity": "sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", - "is-string": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, "engines": { "node": ">=8" } }, - "node_modules/array.prototype.filter": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array.prototype.filter/-/array.prototype.filter-1.0.3.tgz", - "integrity": "sha512-VizNcj/RGJiUyQBgzwxzE5oHdeuXY5hSbbmKMlphj1cy1Vl7Pn2asCGbSrru6hSQjmCzqTBPVWAF/whmEOVHbw==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-array-method-boxes-properly": "^1.0.0", - "is-string": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.findlastindex": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.4.tgz", - "integrity": "sha512-hzvSHUshSpCflDR1QMUBLHGHP1VIEBegT4pix9H/Z92Xw3ySoy6c2qh7lJWTJnRJ8JCZ9bJNCgTyYaJGcJu6xQ==", - "dependencies": { - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.3.0", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flat": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", - "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flatmap": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", - "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", - "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", - "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.2.1", - "get-intrinsic": "^1.2.3", - "is-array-buffer": "^3.0.4", - "is-shared-array-buffer": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/arrify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", @@ -2254,25 +1681,12 @@ "node": ">=0.10.0" } }, - "node_modules/ast-types-flow": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", - "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==" - }, "node_modules/async": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", "dev": true }, - "node_modules/asynciterator.prototype": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/asynciterator.prototype/-/asynciterator.prototype-1.0.0.tgz", - "integrity": "sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg==", - "dependencies": { - "has-symbols": "^1.0.3" - } - }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -2288,28 +1702,6 @@ "node": ">= 4.0.0" } }, - "node_modules/available-typed-arrays": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", - "dependencies": { - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/axe-core": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.7.0.tgz", - "integrity": "sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ==", - "engines": { - "node": ">=4" - } - }, "node_modules/axios": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz", @@ -2321,18 +1713,11 @@ "proxy-from-env": "^1.1.0" } }, - "node_modules/axobject-query": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", - "integrity": "sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==", - "dependencies": { - "dequal": "^2.0.3" - } - }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true }, "node_modules/base64-js": { "version": "1.5.1", @@ -2433,6 +1818,7 @@ "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" @@ -2442,6 +1828,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, "dependencies": { "fill-range": "^7.0.1" }, @@ -2479,22 +1866,11 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, - "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==", - "peer": 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, "dependencies": { "semver": "^7.0.0" } @@ -2543,28 +1919,11 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, "engines": { "node": ">=6" } @@ -2599,6 +1958,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -2735,6 +2095,7 @@ "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" }, @@ -2745,7 +2106,8 @@ "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==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/color-support": { "version": "1.1.3", @@ -2812,7 +2174,8 @@ "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true }, "node_modules/concat-stream": { "version": "2.0.0", @@ -3044,6 +2407,7 @@ "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -3057,6 +2421,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, "dependencies": { "isexe": "^2.0.0" }, @@ -3088,11 +2453,6 @@ "node": ">=4" } }, - "node_modules/damerau-levenshtein": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", - "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==" - }, "node_modules/dargs": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/dargs/-/dargs-7.0.0.tgz", @@ -3115,6 +2475,7 @@ "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, "dependencies": { "ms": "2.1.2" }, @@ -3167,11 +2528,6 @@ "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", "dev": true }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" - }, "node_modules/defaults": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", @@ -3184,22 +2540,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/define-lazy-prop": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", @@ -3209,22 +2549,6 @@ "node": ">=8" } }, - "node_modules/define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/del": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/del/-/del-6.1.1.tgz", @@ -3324,14 +2648,6 @@ "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", "dev": true }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "engines": { - "node": ">=6" - } - }, "node_modules/detect-indent": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-5.0.0.tgz", @@ -3345,6 +2661,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, "dependencies": { "path-type": "^4.0.0" }, @@ -3352,17 +2669,6 @@ "node": ">=8" } }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/dot-prop": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz", @@ -3500,146 +2806,6 @@ "is-arrayish": "^0.2.1" } }, - "node_modules/es-abstract": { - "version": "1.22.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.5.tgz", - "integrity": "sha512-oW69R+4q2wG+Hc3KZePPZxOiisRIqfKBVo/HLx94QcJeWGU/8sZhCvc829rd1kS366vlJbzBfXf9yWwf0+Ko7w==", - "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "arraybuffer.prototype.slice": "^1.0.3", - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "es-set-tostringtag": "^2.0.3", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.4", - "get-symbol-description": "^1.0.2", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.3", - "has-symbols": "^1.0.3", - "hasown": "^2.0.1", - "internal-slot": "^1.0.7", - "is-array-buffer": "^3.0.4", - "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.3", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.3", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.13", - "is-weakref": "^1.0.2", - "object-inspect": "^1.13.1", - "object-keys": "^1.1.1", - "object.assign": "^4.1.5", - "regexp.prototype.flags": "^1.5.2", - "safe-array-concat": "^1.1.0", - "safe-regex-test": "^1.0.3", - "string.prototype.trim": "^1.2.8", - "string.prototype.trimend": "^1.0.7", - "string.prototype.trimstart": "^1.0.7", - "typed-array-buffer": "^1.0.2", - "typed-array-byte-length": "^1.0.1", - "typed-array-byte-offset": "^1.0.2", - "typed-array-length": "^1.0.5", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.14" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-array-method-boxes-properly": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", - "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==" - }, - "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", - "dependencies": { - "get-intrinsic": "^1.2.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-iterator-helpers": { - "version": "1.0.17", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.17.tgz", - "integrity": "sha512-lh7BsUqelv4KUbR5a/ZTaGGIMLCjPGPqJ6q+Oq24YP0RdyptX1uzm4vvaqzk7Zx3bpl/76YLTTDj9L7uYQ92oQ==", - "dependencies": { - "asynciterator.prototype": "^1.0.0", - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.22.4", - "es-errors": "^1.3.0", - "es-set-tostringtag": "^2.0.2", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "globalthis": "^1.0.3", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.7", - "iterator.prototype": "^1.1.2", - "safe-array-concat": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", - "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", - "dependencies": { - "get-intrinsic": "^1.2.4", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-shim-unscopables": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", - "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", - "dependencies": { - "hasown": "^2.0.0" - } - }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -3658,687 +2824,451 @@ "node": ">=0.8.0" } }, - "node_modules/eslint": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", - "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.0", - "@humanwhocodes/config-array": "^0.11.14", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@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.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", - "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", - "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.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, "bin": { - "eslint": "bin/eslint.js" + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">=4" } }, - "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==", - "peer": true, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "dev": true, "engines": { - "node": ">=12" - }, - "peerDependencies": { - "eslint": ">=6.0.0" + "node": ">=6" } }, - "node_modules/eslint-config-prettier": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", - "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", - "bin": { - "eslint-config-prettier": "bin/cli.js" - }, - "peerDependencies": { - "eslint": ">=7.0.0" - } + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true }, - "node_modules/eslint-config-standard": { - "version": "17.1.0", - "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-17.1.0.tgz", - "integrity": "sha512-IwHwmaBNtDK4zDHQukFDW5u/aTb8+meQWZvNFWkiGmbWjD6bqyuSSBxxXKkCftCUzc1zwCH2m/baCNDLGmuO5Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "eslint": "^8.0.1", - "eslint-plugin-import": "^2.25.2", - "eslint-plugin-n": "^15.0.0 || ^16.0.0 ", - "eslint-plugin-promise": "^6.0.0" + "node": ">=0.8.x" } }, - "node_modules/eslint-config-standard-with-typescript": { - "version": "39.1.1", - "resolved": "https://registry.npmjs.org/eslint-config-standard-with-typescript/-/eslint-config-standard-with-typescript-39.1.1.tgz", - "integrity": "sha512-t6B5Ep8E4I18uuoYeYxINyqcXb2UbC0SOOTxRtBSt2JUs+EzeXbfe2oaiPs71AIdnoWhXDO2fYOHz8df3kV84A==", + "node_modules/execa": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.0.0.tgz", + "integrity": "sha512-ov6w/2LCiuyO4RLYGdpFGjkcs0wMTgGE8PrkTHikeUy5iJekXyPIKUjifk5CsE0pt7sMCrMZ3YNqoCj6idQOnQ==", + "dev": true, "dependencies": { - "@typescript-eslint/parser": "^6.4.0", - "eslint-config-standard": "17.1.0" + "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" }, - "peerDependencies": { - "@typescript-eslint/eslint-plugin": "^6.4.0", - "eslint": "^8.0.1", - "eslint-plugin-import": "^2.25.2", - "eslint-plugin-n": "^15.0.0 || ^16.0.0 ", - "eslint-plugin-promise": "^6.0.0", - "typescript": "*" - } - }, - "node_modules/eslint-import-resolver-node": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", - "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", - "dependencies": { - "debug": "^3.2.7", - "is-core-module": "^2.13.0", - "resolve": "^1.22.4" + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/eslint-import-resolver-node/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dependencies": { - "ms": "^2.1.1" - } + "node_modules/exponential-backoff": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.1.tgz", + "integrity": "sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==", + "dev": true }, - "node_modules/eslint-module-utils": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.1.tgz", - "integrity": "sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==", + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, "dependencies": { - "debug": "^3.2.7" + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" }, "engines": { "node": ">=4" - }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - } - } - }, - "node_modules/eslint-module-utils/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dependencies": { - "ms": "^2.1.1" } }, - "node_modules/eslint-plugin-es": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", - "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", + "node_modules/external-editor/node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, "dependencies": { - "eslint-utils": "^2.0.0", - "regexpp": "^3.0.0" + "os-tmpdir": "~1.0.2" }, "engines": { - "node": ">=8.10.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=4.19.1" + "node": ">=0.6.0" } }, - "node_modules/eslint-plugin-es-x": { - "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==", - "peer": true, + "node_modules/fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "dev": true, "dependencies": { - "@eslint-community/eslint-utils": "^4.1.2", - "@eslint-community/regexpp": "^4.6.0", - "eslint-compat-utils": "^0.1.2" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ota-meshi" - }, - "peerDependencies": { - "eslint": ">=8" - } - }, - "node_modules/eslint-plugin-import": { - "version": "2.29.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz", - "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==", - "dependencies": { - "array-includes": "^3.1.7", - "array.prototype.findlastindex": "^1.2.3", - "array.prototype.flat": "^1.3.2", - "array.prototype.flatmap": "^1.3.2", - "debug": "^3.2.7", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.9", - "eslint-module-utils": "^2.8.0", - "hasown": "^2.0.0", - "is-core-module": "^2.13.1", - "is-glob": "^4.0.3", - "minimatch": "^3.1.2", - "object.fromentries": "^2.0.7", - "object.groupby": "^1.0.1", - "object.values": "^1.1.7", - "semver": "^6.3.1", - "tsconfig-paths": "^3.15.0" + "@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" }, "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + "node": ">=8.6.0" } }, - "node_modules/eslint-plugin-import/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, "dependencies": { - "ms": "^2.1.1" + "reusify": "^1.0.4" } }, - "node_modules/eslint-plugin-import/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, "dependencies": { - "esutils": "^2.0.2" + "escape-string-regexp": "^1.0.5" }, "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint-plugin-import/node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dependencies": { - "minimist": "^1.2.0" + "node": ">=8" }, - "bin": { - "json5": "lib/cli.js" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint-plugin-import/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, + "node_modules/file-url": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/file-url/-/file-url-3.0.0.tgz", + "integrity": "sha512-g872QGsHexznxkIAdK8UiZRe7SkE6kvylShU4Nsj8NvfvZag7S0QuQ4IgvPDkk75HxgjIVDwycFTDAgIiO4nDA==", + "dev": true, "engines": { - "node": "*" + "node": ">=8" } }, - "node_modules/eslint-plugin-import/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "bin": { - "semver": "bin/semver.js" + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dev": true, + "dependencies": { + "minimatch": "^5.0.1" } }, - "node_modules/eslint-plugin-import/node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "engines": { - "node": ">=4" + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" } }, - "node_modules/eslint-plugin-import/node_modules/tsconfig-paths": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", - "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" } }, - "node_modules/eslint-plugin-jest": { - "version": "27.9.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-27.9.0.tgz", - "integrity": "sha512-QIT7FH7fNmd9n4se7FFKHbsLKGQiw885Ds6Y/sxKgCZ6natwCsXdgPOADnYVxN2QrRweF0FZWbJ6S7Rsn7llug==", + "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, "dependencies": { - "@typescript-eslint/utils": "^5.10.0" + "to-regex-range": "^5.0.1" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@typescript-eslint/eslint-plugin": "^5.0.0 || ^6.0.0 || ^7.0.0", - "eslint": "^7.0.0 || ^8.0.0", - "jest": "*" - }, - "peerDependenciesMeta": { - "@typescript-eslint/eslint-plugin": { - "optional": true - }, - "jest": { - "optional": true - } + "node": ">=8" } }, - "node_modules/eslint-plugin-jest/node_modules/@typescript-eslint/scope-manager": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", - "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, "dependencies": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=10" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint-plugin-jest/node_modules/@typescript-eslint/types": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", - "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" } }, - "node_modules/eslint-plugin-jest/node_modules/@typescript-eslint/typescript-estree": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", - "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", - "dependencies": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, + "node_modules/follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">=4.0" }, "peerDependenciesMeta": { - "typescript": { + "debug": { "optional": true } } }, - "node_modules/eslint-plugin-jest/node_modules/@typescript-eslint/utils": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", - "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", + "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": { - "@eslint-community/eslint-utils": "^4.2.0", - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/typescript-estree": "5.62.0", - "eslint-scope": "^5.1.1", - "semver": "^7.3.7" + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=14" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/eslint-plugin-jest/node_modules/@typescript-eslint/visitor-keys": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", - "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", - "dependencies": { - "@typescript-eslint/types": "5.62.0", - "eslint-visitor-keys": "^3.3.0" - }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.0.2.tgz", + "integrity": "sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q==", + "dev": true, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=14" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/eslint-plugin-jest/node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dev": true, "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" }, "engines": { - "node": ">=8.0.0" + "node": ">= 6" } }, - "node_modules/eslint-plugin-jest/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "engines": { - "node": ">=4.0" - } + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true }, - "node_modules/eslint-plugin-jsx-a11y": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.8.0.tgz", - "integrity": "sha512-Hdh937BS3KdwwbBaKd5+PLCOmYY6U4f2h9Z2ktwtNKvIdIEu137rjYbcb9ApSbVJfWxANNuiKTD/9tOKjK9qOA==", - "dependencies": { - "@babel/runtime": "^7.23.2", - "aria-query": "^5.3.0", - "array-includes": "^3.1.7", - "array.prototype.flatmap": "^1.3.2", - "ast-types-flow": "^0.0.8", - "axe-core": "=4.7.0", - "axobject-query": "^3.2.1", - "damerau-levenshtein": "^1.0.8", - "emoji-regex": "^9.2.2", - "es-iterator-helpers": "^1.0.15", - "hasown": "^2.0.0", - "jsx-ast-utils": "^3.3.5", - "language-tags": "^1.0.9", - "minimatch": "^3.1.2", - "object.entries": "^1.1.7", - "object.fromentries": "^2.0.7" + "node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, "engines": { - "node": ">=4.0" - }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + "node": ">=10" } }, - "node_modules/eslint-plugin-jsx-a11y/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" - }, - "node_modules/eslint-plugin-jsx-a11y/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/fs-minipass": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.2.tgz", + "integrity": "sha512-2GAfyfoaCDRrM6jaOS3UsBts8yJ55VioXdWcOL7dK9zdAuKT71+WBA4ifnNYqVjYv+4SsPxjK0JT4yIIn4cA/g==", + "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" + "minipass": "^5.0.0" }, "engines": { - "node": "*" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/eslint-plugin-n": { - "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==", - "peer": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "builtins": "^5.0.1", - "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.3" - }, - "engines": { - "node": ">=16.0.0" - }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=7.0.0" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint-plugin-n/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==", - "peer": true, + "node_modules/gauge": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", + "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", + "dev": true, "dependencies": { - "yallist": "^4.0.0" + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" }, "engines": { - "node": ">=10" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "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==", - "peer": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, + "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, "engines": { - "node": "*" + "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/eslint-plugin-n/node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "peer": true, + "node_modules/get-pkg-repo": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/get-pkg-repo/-/get-pkg-repo-4.2.1.tgz", + "integrity": "sha512-2+QbHjFRfGB74v/pYWjd5OhU3TDIC2Gv/YKUTk/tCvAz0pkn/Mz6P3uByuBimLOcPvN2jYdScl3xGFSrx0jEcA==", + "dev": true, "dependencies": { - "lru-cache": "^6.0.0" + "@hutson/parse-repository-url": "^3.0.0", + "hosted-git-info": "^4.0.0", + "through2": "^2.0.0", + "yargs": "^16.2.0" }, "bin": { - "semver": "bin/semver.js" + "get-pkg-repo": "src/cli.js" }, "engines": { - "node": ">=10" + "node": ">=6.9.0" } }, - "node_modules/eslint-plugin-node": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", - "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", + "node_modules/get-pkg-repo/node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, "dependencies": { - "eslint-plugin-es": "^3.0.0", - "eslint-utils": "^2.0.0", - "ignore": "^5.1.1", - "minimatch": "^3.0.4", - "resolve": "^1.10.1", - "semver": "^6.1.0" + "lru-cache": "^6.0.0" }, "engines": { - "node": ">=8.10.0" - }, - "peerDependencies": { - "eslint": ">=5.16.0" - } - }, - "node_modules/eslint-plugin-node/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "bin": { - "semver": "bin/semver.js" + "node": ">=10" } }, - "node_modules/eslint-plugin-prettier": { - "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==", + "node_modules/get-pkg-repo/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==", + "dev": true, "dependencies": { - "prettier-linter-helpers": "^1.0.0", - "synckit": "^0.8.6" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint-plugin-prettier" - }, - "peerDependencies": { - "@types/eslint": ">=8.0.0", - "eslint": ">=8.0.0", - "eslint-config-prettier": "*", - "prettier": ">=3.0.0" - }, - "peerDependenciesMeta": { - "@types/eslint": { - "optional": true - }, - "eslint-config-prettier": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-promise": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.1.1.tgz", - "integrity": "sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig==", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "yallist": "^4.0.0" }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - } - }, - "node_modules/eslint-plugin-react-hooks": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", - "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", "engines": { "node": ">=10" - }, - "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" } }, - "node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "node_modules/get-pkg-repo/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, - "node_modules/eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "node_modules/get-pkg-repo/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 + }, + "node_modules/get-pkg-repo/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, "dependencies": { - "eslint-visitor-keys": "^1.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" + "safe-buffer": "~5.1.0" } }, - "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "engines": { - "node": ">=4" + "node_modules/get-pkg-repo/node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" } }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "node_modules/get-port": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", + "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==", + "dev": true, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=8" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "node_modules/get-stream": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.0.tgz", + "integrity": "sha512-A1B3Bh1UmL0bidM/YX2NsCOTnGJePL9rO/M+Mw3m9f2gUpfokS0hi5Eah0WSUEWZdZhIZtMjkIYS7mDfOqNHbg==", + "dev": true, "engines": { "node": ">=10" }, @@ -4346,653 +3276,623 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/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==", + "node_modules/git-raw-commits": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.11.tgz", + "integrity": "sha512-VnctFhw+xfj8Va1xtfEqCUD2XDrbAPSJx+hSrE5K7fGdjZruW7XV+QOrN7LF/RJyvspRiD2I0asWsxFp0ya26A==", + "dev": true, "dependencies": { - "is-glob": "^4.0.3" + "dargs": "^7.0.0", + "lodash": "^4.17.15", + "meow": "^8.0.0", + "split2": "^3.0.0", + "through2": "^4.0.0" }, - "engines": { - "node": ">=10.13.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==", - "dependencies": { - "brace-expansion": "^1.1.7" + "bin": { + "git-raw-commits": "cli.js" }, "engines": { - "node": "*" + "node": ">=10" } }, - "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "node_modules/git-remote-origin-url": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/git-remote-origin-url/-/git-remote-origin-url-2.0.0.tgz", + "integrity": "sha512-eU+GGrZgccNJcsDH5LkXR3PB9M958hxc7sbA8DFJjrv9j4L2P/eZfKhM+QD6wyzpiv+b1BpK0XrYCxkovtjSLw==", + "dev": true, "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" + "gitconfiglocal": "^1.0.0", + "pify": "^2.3.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">=4" } }, - "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==", + "node_modules/git-remote-origin-url/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, "engines": { - "node": ">=4" + "node": ">=0.10.0" } }, - "node_modules/esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "node_modules/git-semver-tags": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/git-semver-tags/-/git-semver-tags-4.1.1.tgz", + "integrity": "sha512-OWyMt5zBe7xFs8vglMmhM9lRQzCWL3WjHtxNNfJTMngGym7pC1kh8sP6jevfydJ6LP3ZvGxfb6ABYgPUM0mtsA==", + "dev": true, "dependencies": { - "estraverse": "^5.1.0" + "meow": "^8.0.0", + "semver": "^6.0.0" + }, + "bin": { + "git-semver-tags": "cli.js" }, "engines": { - "node": ">=0.10" + "node": ">=10" } }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "engines": { - "node": ">=4.0" + "node_modules/git-semver-tags/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" } }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "engines": { - "node": ">=0.10.0" + "node_modules/git-up": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/git-up/-/git-up-7.0.0.tgz", + "integrity": "sha512-ONdIrbBCFusq1Oy0sC71F5azx8bVkvtZtMJAsv+a6lz5YAmbNnLD6HAB4gptHZVLPR8S2/kVN6Gab7lryq5+lQ==", + "dev": true, + "dependencies": { + "is-ssh": "^1.4.0", + "parse-url": "^8.1.0" } }, - "node_modules/event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "node_modules/git-url-parse": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-13.1.0.tgz", + "integrity": "sha512-5FvPJP/70WkIprlUZ33bm4UAaFdjcLkJLpWft1BeZKqwR0uhhNGoKwlUaPtVb4LxCSQ++erHapRak9kWGj+FCA==", "dev": true, - "engines": { - "node": ">=6" + "dependencies": { + "git-up": "^7.0.0" } }, - "node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "dev": true - }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "node_modules/gitconfiglocal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gitconfiglocal/-/gitconfiglocal-1.0.0.tgz", + "integrity": "sha512-spLUXeTAVHxDtKsJc8FkFVgFtMdEN9qPGpL23VfSHx4fP4+Ds097IXLvymbnDH8FnmxX5Nr9bPw3A+AQ6mWEaQ==", "dev": true, - "engines": { - "node": ">=0.8.x" + "dependencies": { + "ini": "^1.3.2" } }, - "node_modules/execa": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.0.0.tgz", - "integrity": "sha512-ov6w/2LCiuyO4RLYGdpFGjkcs0wMTgGE8PrkTHikeUy5iJekXyPIKUjifk5CsE0pt7sMCrMZ3YNqoCj6idQOnQ==", + "node_modules/glob": { + "version": "10.2.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.2.7.tgz", + "integrity": "sha512-jTKehsravOJo8IJxUGfZILnkvVJM/MOfHRs8QcXolVef2zNI9Tqyy5+SeuOAZd3upViEZQLyFpQhYiHLrMUNmA==", "dev": true, "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" + "foreground-child": "^3.1.0", + "jackspeak": "^2.0.3", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2", + "path-scurry": "^1.7.0" + }, + "bin": { + "glob": "dist/cjs/src/bin.js" }, "engines": { - "node": ">=10" + "node": ">=16 || 14 >=14.17" }, "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/exponential-backoff": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.1.tgz", - "integrity": "sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==", - "dev": true - }, - "node_modules/external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "dependencies": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" + "is-glob": "^4.0.1" }, "engines": { - "node": ">=4" + "node": ">= 6" } }, - "node_modules/external-editor/node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "dependencies": { - "os-tmpdir": "~1.0.2" - }, - "engines": { - "node": ">=0.6.0" + "balanced-match": "^1.0.0" } }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "node_modules/fast-diff": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", - "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==" - }, - "node_modules/fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "node_modules/glob/node_modules/minimatch": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz", + "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==", + "dev": true, "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" - }, - "node_modules/fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", - "dependencies": { - "reusify": "^1.0.4" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, "dependencies": { - "escape-string-regexp": "^1.0.5" + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "node_modules/handlebars": { + "version": "4.7.7", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", + "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", + "dev": true, "dependencies": { - "flat-cache": "^3.0.4" + "minimist": "^1.2.5", + "neo-async": "^2.6.0", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" } }, - "node_modules/file-url": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/file-url/-/file-url-3.0.0.tgz", - "integrity": "sha512-g872QGsHexznxkIAdK8UiZRe7SkE6kvylShU4Nsj8NvfvZag7S0QuQ4IgvPDkk75HxgjIVDwycFTDAgIiO4nDA==", + "node_modules/hard-rejection": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", + "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", "dev": true, "engines": { - "node": ">=8" + "node": ">=6" } }, - "node_modules/filelist": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", - "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "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, - "dependencies": { - "minimatch": "^5.0.1" + "engines": { + "node": ">=8" } }, - "node_modules/filelist/node_modules/brace-expansion": { + "node_modules/has-unicode": { "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" - } + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "dev": true }, - "node_modules/filelist/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "node_modules/hasown": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz", + "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==", "dev": true, "dependencies": { - "brace-expansion": "^2.0.1" + "function-bind": "^1.1.2" }, "engines": { - "node": ">=10" + "node": ">= 0.4" } }, - "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==", + "node_modules/hosted-git-info": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-6.1.1.tgz", + "integrity": "sha512-r0EI+HBMcXadMrugk0GCQ+6BQV39PiWAZVfq7oIckeGiN7sjRGyQxPdft3nQekFTCQbYxLBH+/axZMeH8UX6+w==", + "dev": true, "dependencies": { - "to-regex-range": "^5.0.1" + "lru-cache": "^7.5.1" }, "engines": { - "node": ">=8" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "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==", + "node_modules/http-cache-semantics": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", + "dev": true + }, + "node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 6" } }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", "dev": true, - "bin": { - "flat": "cli.js" - } - }, - "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==", "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" + "agent-base": "6", + "debug": "4" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">= 6" } }, - "node_modules/flat-cache/node_modules/glob": { - "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.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=10.17.0" } }, - "node_modules/flat-cache/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" + "ms": "^2.0.0" } }, - "node_modules/flat-cache/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" - }, + "node_modules/husky": { + "version": "9.0.11", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.0.11.tgz", + "integrity": "sha512-AB6lFlbwwyIqMdHYhwPe+kjOC3Oc5P3nThEoW/AaO2BX3vJDjWPFxYLxokUZOo6RNX20He3AaT8sESs9NJcmEw==", + "dev": true, "bin": { - "rimraf": "bin.js" + "husky": "bin.mjs" + }, + "engines": { + "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/typicode" } }, - "node_modules/flatted": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", - "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==" + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } }, - "node_modules/follow-redirects": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", "dev": true, "funding": [ { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" } - ], + ] + }, + "node_modules/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true, "engines": { - "node": ">=4.0" + "node": ">= 4" + } + }, + "node_modules/ignore-walk": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-5.0.1.tgz", + "integrity": "sha512-yemi4pMf51WKT7khInJqAvsIGzoqYXblnsz0ql8tM+yi1EKYTY1evX4NAbJrLL/Aanr2HyZeluqU+Oi7MGHokw==", + "dev": true, + "dependencies": { + "minimatch": "^5.0.1" }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "node_modules/ignore-walk/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, "dependencies": { - "is-callable": "^1.1.3" + "balanced-match": "^1.0.0" } }, - "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==", + "node_modules/ignore-walk/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^4.0.1" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=10" } }, - "node_modules/foreground-child/node_modules/signal-exit": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.0.2.tgz", - "integrity": "sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q==", + "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, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, "engines": { - "node": ">=14" + "node": ">=6" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/form-data": { + "node_modules/import-fresh/node_modules/resolve-from": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, "engines": { - "node": ">= 6" + "node": ">=4" } }, - "node_modules/fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "dev": true - }, - "node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "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, "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" }, "engines": { - "node": ">=10" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/fs-minipass": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.2.tgz", - "integrity": "sha512-2GAfyfoaCDRrM6jaOS3UsBts8yJ55VioXdWcOL7dK9zdAuKT71+WBA4ifnNYqVjYv+4SsPxjK0JT4yIIn4cA/g==", + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, - "dependencies": { - "minipass": "^5.0.0" - }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=0.8.19" } }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" } }, - "node_modules/function.prototype.name": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", - "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "functions-have-names": "^1.2.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "node_modules/infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "dev": true }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" } }, - "node_modules/gauge": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", - "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "node_modules/init-package-json": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/init-package-json/-/init-package-json-3.0.2.tgz", + "integrity": "sha512-YhlQPEjNFqlGdzrBfDNRLhvoSgX7iQRgSxgsNknRQ9ITXFT7UMfVMWhBTOh2Y+25lRnGrv5Xz8yZwQ3ACR6T3A==", "dev": true, "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.3", - "console-control-strings": "^1.1.0", - "has-unicode": "^2.0.1", - "signal-exit": "^3.0.7", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.5" + "npm-package-arg": "^9.0.1", + "promzard": "^0.3.0", + "read": "^1.0.7", + "read-package-json": "^5.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4", + "validate-npm-package-name": "^4.0.0" }, "engines": { "node": "^12.13.0 || ^14.15.0 || >=16.0.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==", + "node_modules/init-package-json/node_modules/hosted-git-info": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-5.2.1.tgz", + "integrity": "sha512-xIcQYMnhcx2Nr4JTjsFmwwnr9vldugPy9uVm0o87bjqqWMv9GaqsTeT+i99wTl0mk1uLxJtHxLb8kymqTENQsw==", "dev": true, + "dependencies": { + "lru-cache": "^7.5.1" + }, "engines": { - "node": "6.* || 8.* || >= 10.*" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "node_modules/init-package-json/node_modules/npm-package-arg": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-9.1.2.tgz", + "integrity": "sha512-pzd9rLEx4TfNJkovvlBSLGhq31gGu2QDexFPWT19yCDh0JgnRhlBLNo5759N0AJmBk+kQ9Y/hXoLnlgFD+ukmg==", + "dev": true, "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "hosted-git-info": "^5.0.0", + "proc-log": "^2.0.1", + "semver": "^7.3.5", + "validate-npm-package-name": "^4.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/get-pkg-repo": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/get-pkg-repo/-/get-pkg-repo-4.2.1.tgz", - "integrity": "sha512-2+QbHjFRfGB74v/pYWjd5OhU3TDIC2Gv/YKUTk/tCvAz0pkn/Mz6P3uByuBimLOcPvN2jYdScl3xGFSrx0jEcA==", + "node_modules/init-package-json/node_modules/proc-log": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-2.0.1.tgz", + "integrity": "sha512-Kcmo2FhfDTXdcbfDH76N7uBYHINxc/8GW7UAVuVP9I+Va3uHSerrnKV6dLooga/gh7GlgzuCCr/eoldnL1muGw==", "dev": true, - "dependencies": { - "@hutson/parse-repository-url": "^3.0.0", - "hosted-git-info": "^4.0.0", - "through2": "^2.0.0", - "yargs": "^16.2.0" - }, - "bin": { - "get-pkg-repo": "src/cli.js" - }, "engines": { - "node": ">=6.9.0" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/get-pkg-repo/node_modules/hosted-git-info": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", - "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "node_modules/inquirer": { + "version": "8.2.5", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.5.tgz", + "integrity": "sha512-QAgPDQMEgrDssk1XiwwHoOGYF9BAbUcc1+j+FhEvaOt8/cKRqyLn0U5qA6F74fGhTMGxf92pOvPBeh29jQJDTQ==", "dev": true, "dependencies": { - "lru-cache": "^6.0.0" + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.5.5", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6", + "wrap-ansi": "^7.0.0" }, "engines": { - "node": ">=10" + "node": ">=12.0.0" } }, - "node_modules/get-pkg-repo/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==", + "node_modules/inquirer/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": { - "yallist": "^4.0.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/get-pkg-repo/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } + "node_modules/ip": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", + "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==", + "dev": true }, - "node_modules/get-pkg-repo/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "dev": true }, - "node_modules/get-pkg-repo/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "node_modules/is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", "dev": true, "dependencies": { - "safe-buffer": "~5.1.0" + "ci-info": "^2.0.0" + }, + "bin": { + "is-ci": "bin.js" } }, - "node_modules/get-pkg-repo/node_modules/through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", "dev": true, "dependencies": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/get-port": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", - "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==", + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", "dev": true, + "bin": { + "is-docker": "cli.js" + }, "engines": { "node": ">=8" }, @@ -5000,1296 +3900,239 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/get-stream": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.0.tgz", - "integrity": "sha512-A1B3Bh1UmL0bidM/YX2NsCOTnGJePL9rO/M+Mw3m9f2gUpfokS0hi5Eah0WSUEWZdZhIZtMjkIYS7mDfOqNHbg==", + "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, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, - "node_modules/get-symbol-description": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", - "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", - "dependencies": { - "call-bind": "^1.0.5", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4" - }, + "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": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/get-tsconfig": { - "version": "4.7.3", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.3.tgz", - "integrity": "sha512-ZvkrzoUA0PQZM6fy6+/Hce561s+faD1rsNwhnO5FelNjyy7EMGJ3Rz1AQ8GYDWjhRs/7dBLOEJvhK8MiEJOAFg==", - "peer": true, + "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, "dependencies": { - "resolve-pkg-maps": "^1.0.0" + "is-extglob": "^2.1.1" }, - "funding": { - "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/git-raw-commits": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.11.tgz", - "integrity": "sha512-VnctFhw+xfj8Va1xtfEqCUD2XDrbAPSJx+hSrE5K7fGdjZruW7XV+QOrN7LF/RJyvspRiD2I0asWsxFp0ya26A==", + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", "dev": true, - "dependencies": { - "dargs": "^7.0.0", - "lodash": "^4.17.15", - "meow": "^8.0.0", - "split2": "^3.0.0", - "through2": "^4.0.0" - }, - "bin": { - "git-raw-commits": "cli.js" - }, "engines": { - "node": ">=10" + "node": ">=8" } }, - "node_modules/git-remote-origin-url": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/git-remote-origin-url/-/git-remote-origin-url-2.0.0.tgz", - "integrity": "sha512-eU+GGrZgccNJcsDH5LkXR3PB9M958hxc7sbA8DFJjrv9j4L2P/eZfKhM+QD6wyzpiv+b1BpK0XrYCxkovtjSLw==", + "node_modules/is-lambda": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", + "dev": true + }, + "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, - "dependencies": { - "gitconfiglocal": "^1.0.0", - "pify": "^2.3.0" - }, "engines": { - "node": ">=4" + "node": ">=0.12.0" } }, - "node_modules/git-remote-origin-url/node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "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, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/git-semver-tags": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/git-semver-tags/-/git-semver-tags-4.1.1.tgz", - "integrity": "sha512-OWyMt5zBe7xFs8vglMmhM9lRQzCWL3WjHtxNNfJTMngGym7pC1kh8sP6jevfydJ6LP3ZvGxfb6ABYgPUM0mtsA==", + "node_modules/is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", "dev": true, - "dependencies": { - "meow": "^8.0.0", - "semver": "^6.0.0" - }, - "bin": { - "git-semver-tags": "cli.js" - }, "engines": { - "node": ">=10" + "node": ">=6" } }, - "node_modules/git-semver-tags/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "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, - "bin": { - "semver": "bin/semver.js" + "engines": { + "node": ">=8" } }, - "node_modules/git-up": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/git-up/-/git-up-7.0.0.tgz", - "integrity": "sha512-ONdIrbBCFusq1Oy0sC71F5azx8bVkvtZtMJAsv+a6lz5YAmbNnLD6HAB4gptHZVLPR8S2/kVN6Gab7lryq5+lQ==", + "node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", "dev": true, - "dependencies": { - "is-ssh": "^1.4.0", - "parse-url": "^8.1.0" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/git-url-parse": { - "version": "13.1.0", - "resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-13.1.0.tgz", - "integrity": "sha512-5FvPJP/70WkIprlUZ33bm4UAaFdjcLkJLpWft1BeZKqwR0uhhNGoKwlUaPtVb4LxCSQ++erHapRak9kWGj+FCA==", + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", "dev": true, - "dependencies": { - "git-up": "^7.0.0" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/gitconfiglocal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/gitconfiglocal/-/gitconfiglocal-1.0.0.tgz", - "integrity": "sha512-spLUXeTAVHxDtKsJc8FkFVgFtMdEN9qPGpL23VfSHx4fP4+Ds097IXLvymbnDH8FnmxX5Nr9bPw3A+AQ6mWEaQ==", + "node_modules/is-ssh": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.4.0.tgz", + "integrity": "sha512-x7+VxdxOdlV3CYpjvRLBv5Lo9OJerlYanjwFrPR9fuGPjCiNiCzFgAWpiLAohSbsnH4ZAys3SBh+hq5rJosxUQ==", "dev": true, "dependencies": { - "ini": "^1.3.2" + "protocols": "^2.0.1" } }, - "node_modules/glob": { - "version": "10.2.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.2.7.tgz", - "integrity": "sha512-jTKehsravOJo8IJxUGfZILnkvVJM/MOfHRs8QcXolVef2zNI9Tqyy5+SeuOAZd3upViEZQLyFpQhYiHLrMUNmA==", + "node_modules/is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", "dev": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^2.0.3", - "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2", - "path-scurry": "^1.7.0" - }, - "bin": { - "glob": "dist/cjs/src/bin.js" - }, "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=8" } }, - "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==", + "node_modules/is-text-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz", + "integrity": "sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w==", + "dev": true, "dependencies": { - "is-glob": "^4.0.1" + "text-extensions": "^1.0.0" }, "engines": { - "node": ">= 6" - } - }, - "node_modules/glob/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" + "node": ">=0.10.0" } }, - "node_modules/glob/node_modules/minimatch": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz", - "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==", + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, "dependencies": { - "type-fest": "^0.20.2" + "is-docker": "^2.0.0" }, "engines": { "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/globals/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, - "node_modules/globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "node_modules/jackspeak": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.2.1.tgz", + "integrity": "sha512-MXbxovZ/Pm42f6cDIDkl3xpwv1AGwObKwfmjs2nQePiy85tP3fatofl3FC1aBsOtP/6fq5SbtgHwWcMsLP+bDw==", + "dev": true, "dependencies": { - "define-properties": "^1.1.3" + "@isaacs/cliui": "^8.0.2" }, "engines": { - "node": ">= 0.4" + "node": ">=14" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" } }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "node_modules/jake": { + "version": "10.8.7", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.7.tgz", + "integrity": "sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w==", + "dev": true, "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" + }, + "bin": { + "jake": "bin/cli.js" }, "engines": { "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "node_modules/jake/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": { - "get-intrinsic": "^1.1.3" + "brace-expansion": "^1.1.7" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": "*" } }, - "node_modules/graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", - "dev": true - }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==" - }, - "node_modules/handlebars": { - "version": "4.7.7", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", - "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", - "dev": true, - "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.0", - "source-map": "^0.6.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "handlebars": "bin/handlebars" - }, - "engines": { - "node": ">=0.4.7" - }, - "optionalDependencies": { - "uglify-js": "^3.1.4" - } - }, - "node_modules/hard-rejection": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", - "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", - "dev": true - }, - "node_modules/hasown": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz", - "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/hosted-git-info": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-6.1.1.tgz", - "integrity": "sha512-r0EI+HBMcXadMrugk0GCQ+6BQV39PiWAZVfq7oIckeGiN7sjRGyQxPdft3nQekFTCQbYxLBH+/axZMeH8UX6+w==", - "dev": true, - "dependencies": { - "lru-cache": "^7.5.1" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/http-cache-semantics": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", - "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", - "dev": true - }, - "node_modules/http-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", - "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", - "dev": true, - "dependencies": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dev": true, - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/humanize-ms": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", - "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", - "dev": true, - "dependencies": { - "ms": "^2.0.0" - } - }, - "node_modules/husky": { - "version": "9.0.11", - "resolved": "https://registry.npmjs.org/husky/-/husky-9.0.11.tgz", - "integrity": "sha512-AB6lFlbwwyIqMdHYhwPe+kjOC3Oc5P3nThEoW/AaO2BX3vJDjWPFxYLxokUZOo6RNX20He3AaT8sESs9NJcmEw==", - "dev": true, - "bin": { - "husky": "bin.mjs" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/typicode" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", - "engines": { - "node": ">= 4" - } - }, - "node_modules/ignore-walk": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-5.0.1.tgz", - "integrity": "sha512-yemi4pMf51WKT7khInJqAvsIGzoqYXblnsz0ql8tM+yi1EKYTY1evX4NAbJrLL/Aanr2HyZeluqU+Oi7MGHokw==", - "dev": true, - "dependencies": { - "minimatch": "^5.0.1" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/ignore-walk/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/ignore-walk/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/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==", - "engines": { - "node": ">=4" - } - }, - "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, - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/infer-owner": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", - "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", - "dev": true - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true - }, - "node_modules/init-package-json": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/init-package-json/-/init-package-json-3.0.2.tgz", - "integrity": "sha512-YhlQPEjNFqlGdzrBfDNRLhvoSgX7iQRgSxgsNknRQ9ITXFT7UMfVMWhBTOh2Y+25lRnGrv5Xz8yZwQ3ACR6T3A==", - "dev": true, - "dependencies": { - "npm-package-arg": "^9.0.1", - "promzard": "^0.3.0", - "read": "^1.0.7", - "read-package-json": "^5.0.0", - "semver": "^7.3.5", - "validate-npm-package-license": "^3.0.4", - "validate-npm-package-name": "^4.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/init-package-json/node_modules/hosted-git-info": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-5.2.1.tgz", - "integrity": "sha512-xIcQYMnhcx2Nr4JTjsFmwwnr9vldugPy9uVm0o87bjqqWMv9GaqsTeT+i99wTl0mk1uLxJtHxLb8kymqTENQsw==", - "dev": true, - "dependencies": { - "lru-cache": "^7.5.1" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/init-package-json/node_modules/npm-package-arg": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-9.1.2.tgz", - "integrity": "sha512-pzd9rLEx4TfNJkovvlBSLGhq31gGu2QDexFPWT19yCDh0JgnRhlBLNo5759N0AJmBk+kQ9Y/hXoLnlgFD+ukmg==", - "dev": true, - "dependencies": { - "hosted-git-info": "^5.0.0", - "proc-log": "^2.0.1", - "semver": "^7.3.5", - "validate-npm-package-name": "^4.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/init-package-json/node_modules/proc-log": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-2.0.1.tgz", - "integrity": "sha512-Kcmo2FhfDTXdcbfDH76N7uBYHINxc/8GW7UAVuVP9I+Va3uHSerrnKV6dLooga/gh7GlgzuCCr/eoldnL1muGw==", - "dev": true, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/inquirer": { - "version": "8.2.5", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.5.tgz", - "integrity": "sha512-QAgPDQMEgrDssk1XiwwHoOGYF9BAbUcc1+j+FhEvaOt8/cKRqyLn0U5qA6F74fGhTMGxf92pOvPBeh29jQJDTQ==", - "dev": true, - "dependencies": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.1", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.21", - "mute-stream": "0.0.8", - "ora": "^5.4.1", - "run-async": "^2.4.0", - "rxjs": "^7.5.5", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/inquirer/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/internal-slot": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", - "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", - "dependencies": { - "es-errors": "^1.3.0", - "hasown": "^2.0.0", - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/ip": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", - "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==", - "dev": true - }, - "node_modules/is-array-buffer": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", - "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, - "node_modules/is-async-function": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", - "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dependencies": { - "has-bigints": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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==", - "peer": 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", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", - "dev": true, - "dependencies": { - "ci-info": "^2.0.0" - }, - "bin": { - "is-ci": "bin.js" - } - }, - "node_modules/is-core-module": { - "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": { - "hasown": "^2.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true, - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-finalizationregistry": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz", - "integrity": "sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==", - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-generator-function": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", - "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-lambda": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", - "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", - "dev": true - }, - "node_modules/is-map": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", - "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-negative-zero": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", - "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-path-cwd": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", - "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "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==", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-set": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", - "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", - "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", - "dependencies": { - "call-bind": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-ssh": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.4.0.tgz", - "integrity": "sha512-x7+VxdxOdlV3CYpjvRLBv5Lo9OJerlYanjwFrPR9fuGPjCiNiCzFgAWpiLAohSbsnH4ZAys3SBh+hq5rJosxUQ==", - "dev": true, - "dependencies": { - "protocols": "^2.0.1" - } - }, - "node_modules/is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-text-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz", - "integrity": "sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w==", - "dev": true, - "dependencies": { - "text-extensions": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", - "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", - "dependencies": { - "which-typed-array": "^1.1.14" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-weakmap": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", - "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakset": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.3.tgz", - "integrity": "sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==", - "dependencies": { - "call-bind": "^1.0.7", - "get-intrinsic": "^1.2.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dev": true, - "dependencies": { - "is-docker": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" - }, - "node_modules/isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/iterator.prototype": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz", - "integrity": "sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==", - "dependencies": { - "define-properties": "^1.2.1", - "get-intrinsic": "^1.2.1", - "has-symbols": "^1.0.3", - "reflect.getprototypeof": "^1.0.4", - "set-function-name": "^2.0.1" - } - }, - "node_modules/jackspeak": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.2.1.tgz", - "integrity": "sha512-MXbxovZ/Pm42f6cDIDkl3xpwv1AGwObKwfmjs2nQePiy85tP3fatofl3FC1aBsOtP/6fq5SbtgHwWcMsLP+bDw==", - "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/jake": { - "version": "10.8.7", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.7.tgz", - "integrity": "sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w==", - "dev": true, - "dependencies": { - "async": "^3.2.3", - "chalk": "^4.0.2", - "filelist": "^1.0.4", - "minimatch": "^3.1.2" - }, - "bin": { - "jake": "bin/cli.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jake/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/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, "dependencies": { "argparse": "^2.0.1" }, @@ -6297,11 +4140,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" - }, "node_modules/json-parse-better-errors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", @@ -6317,16 +4155,6 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "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==" - }, - "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==" - }, "node_modules/json-stringify-nice": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/json-stringify-nice/-/json-stringify-nice-1.1.4.tgz", @@ -6397,20 +4225,6 @@ "node": "*" } }, - "node_modules/jsx-ast-utils": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", - "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", - "dependencies": { - "array-includes": "^3.1.6", - "array.prototype.flat": "^1.3.1", - "object.assign": "^4.1.4", - "object.values": "^1.1.6" - }, - "engines": { - "node": ">=4.0" - } - }, "node_modules/just-diff": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/just-diff/-/just-diff-6.0.2.tgz", @@ -6423,14 +4237,6 @@ "integrity": "sha512-OYTthRfSh55WOItVqwpefPtNt2VdKsq5AnAK6apdtR6yCH8pr0CmSr710J0Mf+WdQy7K/OzMy7K2MgAfdQURDw==", "dev": true }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dependencies": { - "json-buffer": "3.0.1" - } - }, "node_modules/kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -6440,22 +4246,6 @@ "node": ">=0.10.0" } }, - "node_modules/language-subtag-registry": { - "version": "0.3.22", - "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz", - "integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==" - }, - "node_modules/language-tags": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", - "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", - "dependencies": { - "language-subtag-registry": "^0.3.20" - }, - "engines": { - "node": ">=0.10" - } - }, "node_modules/lerna": { "version": "6.6.2", "resolved": "https://registry.npmjs.org/lerna/-/lerna-6.6.2.tgz", @@ -6546,18 +4336,6 @@ "node": "^14.17.0 || >=16.0.0" } }, - "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==", - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/libnpmaccess": { "version": "6.0.4", "resolved": "https://registry.npmjs.org/libnpmaccess/-/libnpmaccess-6.0.4.tgz", @@ -6968,6 +4746,7 @@ "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" }, @@ -6990,11 +4769,6 @@ "integrity": "sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g==", "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==" - }, "node_modules/log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -7309,6 +5083,7 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, "engines": { "node": ">= 8" } @@ -7317,6 +5092,7 @@ "version": "4.0.5", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, "dependencies": { "braces": "^3.0.2", "picomatch": "^2.3.1" @@ -7368,6 +5144,7 @@ "version": "3.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.5.tgz", "integrity": "sha512-tUpxzX0VAzJHjLu0xUfFv1gwVp9ba3IOuRAVH2EGuRW8a5emA2FlACLqiT/lDVtS1W+TGNwqz3sWaNyLgDJWuw==", + "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -7379,6 +5156,7 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -7604,7 +5382,8 @@ "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==" + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true }, "node_modules/multimatch": { "version": "5.0.0", @@ -7640,11 +5419,6 @@ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "dev": 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==" - }, "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -8279,114 +6053,25 @@ "node": ">=12" } }, - "node_modules/nx/node_modules/yargs/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, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "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" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", - "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", - "dependencies": { - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.entries": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.7.tgz", - "integrity": "sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.fromentries": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz", - "integrity": "sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.groupby": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.2.tgz", - "integrity": "sha512-bzBq58S+x+uo0VjurFT0UktpKHOZmv4/xePiOA1nbB9pMqpGK7rUPNgf+1YC+7mE+0HzhTMqNUuCqvKhj6FnBw==", - "dependencies": { - "array.prototype.filter": "^1.0.3", - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.0.0" - } - }, - "node_modules/object.values": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz", - "integrity": "sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==", + "node_modules/nx/node_modules/yargs/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, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=12" } }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, "dependencies": { "wrappy": "1" } @@ -8423,22 +6108,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", - "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" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/ora": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", @@ -8484,6 +6153,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, "dependencies": { "yocto-queue": "^0.1.0" }, @@ -8498,6 +6168,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, "dependencies": { "p-limit": "^3.0.2" }, @@ -8777,6 +6448,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, "dependencies": { "callsites": "^3.0.0" }, @@ -8850,6 +6522,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, "engines": { "node": ">=8" } @@ -8858,6 +6531,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -8866,6 +6540,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, "engines": { "node": ">=8" } @@ -8873,7 +6548,8 @@ "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true }, "node_modules/path-scurry": { "version": "1.9.2", @@ -8904,6 +6580,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, "engines": { "node": ">=8" } @@ -8912,6 +6589,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, "engines": { "node": ">=8.6" }, @@ -8995,14 +6673,6 @@ "node": ">=8" } }, - "node_modules/possible-typed-array-names": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", - "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", - "engines": { - "node": ">= 0.4" - } - }, "node_modules/postcss-selector-parser": { "version": "6.0.13", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz", @@ -9016,39 +6686,6 @@ "node": ">=4" } }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prettier": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", - "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/prettier-linter-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", - "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", - "dependencies": { - "fast-diff": "^1.1.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/pretty-format": { "version": "29.4.3", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.4.3.tgz", @@ -9163,14 +6800,6 @@ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "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==", - "engines": { - "node": ">=6" - } - }, "node_modules/q": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", @@ -9185,6 +6814,7 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, "funding": [ { "type": "github", @@ -9549,59 +7179,6 @@ "node": ">=8" } }, - "node_modules/reflect.getprototypeof": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.5.tgz", - "integrity": "sha512-62wgfC8dJWrmxv44CA36pLDnP6KKl3Vhxb7PL+8+qrrFMMoJij4vgiMP8zV4O8+CBMXY1mHxI5fITGHXFHVmQQ==", - "dependencies": { - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.0.0", - "get-intrinsic": "^1.2.3", - "globalthis": "^1.0.3", - "which-builtin-type": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" - }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", - "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", - "dependencies": { - "call-bind": "^1.0.6", - "define-properties": "^1.2.1", - "es-errors": "^1.3.0", - "set-function-name": "^2.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -9615,6 +7192,7 @@ "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", @@ -9648,15 +7226,6 @@ "node": ">=8" } }, - "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==", - "peer": true, - "funding": { - "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" - } - }, "node_modules/restore-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", @@ -9683,6 +7252,7 @@ "version": "1.0.4", "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" @@ -9770,6 +7340,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, "funding": [ { "type": "github", @@ -9797,28 +7368,6 @@ "tslib": "^2.1.0" } }, - "node_modules/safe-array-concat": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", - "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", - "dependencies": { - "call-bind": "^1.0.7", - "get-intrinsic": "^1.2.4", - "has-symbols": "^1.0.3", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">=0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safe-array-concat/node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" - }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -9839,22 +7388,6 @@ } ] }, - "node_modules/safe-regex-test": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", - "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", - "dependencies": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-regex": "^1.1.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -9865,6 +7398,7 @@ "version": "7.5.2", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.2.tgz", "integrity": "sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ==", + "dev": true, "dependencies": { "lru-cache": "^6.0.0" }, @@ -9879,6 +7413,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, "dependencies": { "yallist": "^4.0.0" }, @@ -9892,36 +7427,6 @@ "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "dev": true }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/set-function-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", - "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/shallow-clone": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", @@ -9938,6 +7443,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, "dependencies": { "shebang-regex": "^3.0.0" }, @@ -9949,27 +7455,11 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, "engines": { "node": ">=8" } }, - "node_modules/side-channel": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", - "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", - "dependencies": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", @@ -9998,6 +7488,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, "engines": { "node": ">=8" } @@ -10182,52 +7673,11 @@ "node": ">=8" } }, - "node_modules/string.prototype.trim": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", - "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimend": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", - "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", - "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -10278,17 +7728,6 @@ "node": ">=8" } }, - "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" - } - }, "node_modules/strong-log-transformer": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/strong-log-transformer/-/strong-log-transformer-2.1.0.tgz", @@ -10310,6 +7749,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -10321,6 +7761,7 @@ "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==", + "dev": true, "engines": { "node": ">= 0.4" }, @@ -10328,21 +7769,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/synckit": { - "version": "0.8.8", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.8.tgz", - "integrity": "sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==", - "dependencies": { - "@pkgr/core": "^0.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/unts" - } - }, "node_modules/tar": { "version": "6.1.11", "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", @@ -10458,11 +7884,6 @@ "node": ">=0.10" } }, - "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==" - }, "node_modules/through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -10541,6 +7962,7 @@ "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, "dependencies": { "is-number": "^7.0.0" }, @@ -10572,17 +7994,6 @@ "node": ">=8" } }, - "node_modules/ts-api-utils": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", - "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", - "engines": { - "node": ">=16" - }, - "peerDependencies": { - "typescript": ">=4.2.0" - } - }, "node_modules/tsconfig-paths": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", @@ -10609,26 +8020,8 @@ "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/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, - "node_modules/tsutils/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true }, "node_modules/tuf-js": { "version": "1.1.7", @@ -10644,17 +8037,6 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "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==", - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/type-fest": { "version": "0.21.3", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", @@ -10667,75 +8049,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/typed-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", - "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", - "dependencies": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/typed-array-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", - "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", - "dependencies": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-byte-offset": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", - "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-length": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.5.tgz", - "integrity": "sha512-yMi0PlwuznKHxKmcpoOdeLwxBoVPkqZxd7q2FgMkmD3bNwvF5VW0+UlUQ1k1vmktTu4Yu13Q0RIxEP8+B+wloA==", - "dependencies": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13", - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", @@ -10746,6 +8059,7 @@ "version": "4.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz", "integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==", + "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -10763,22 +8077,8 @@ "bin": { "uglifyjs": "bin/uglifyjs" }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "dependencies": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=0.8.0" } }, "node_modules/unique-filename": { @@ -10842,14 +8142,6 @@ "yarn": "*" } }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dependencies": { - "punycode": "^2.1.0" - } - }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -10939,86 +8231,6 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-builtin-type": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.3.tgz", - "integrity": "sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==", - "dependencies": { - "function.prototype.name": "^1.1.5", - "has-tostringtag": "^1.0.0", - "is-async-function": "^2.0.0", - "is-date-object": "^1.0.5", - "is-finalizationregistry": "^1.0.2", - "is-generator-function": "^1.0.10", - "is-regex": "^1.1.4", - "is-weakref": "^1.0.2", - "isarray": "^2.0.5", - "which-boxed-primitive": "^1.0.2", - "which-collection": "^1.0.1", - "which-typed-array": "^1.1.9" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-builtin-type/node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" - }, - "node_modules/which-collection": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", - "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", - "dependencies": { - "is-map": "^2.0.3", - "is-set": "^2.0.3", - "is-weakmap": "^2.0.2", - "is-weakset": "^2.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-typed-array": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", - "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/wide-align": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", @@ -11072,7 +8284,8 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true }, "node_modules/write-file-atomic": { "version": "4.0.1", @@ -11190,7 +8403,8 @@ "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true }, "node_modules/yaml": { "version": "1.10.2", @@ -11232,6 +8446,7 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, "engines": { "node": ">=10" }, @@ -11241,11 +8456,6 @@ } }, "dependencies": { - "@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==" - }, "@babel/code-frame": { "version": "7.22.5", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz", @@ -11324,84 +8534,12 @@ } } }, - "@babel/runtime": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.0.tgz", - "integrity": "sha512-Chk32uHMg6TnQdvw2e9IlqPpFX/6NLuK0Ys2PqLb7/gL5uFn9mXvK715FGLlOLQrcO4qIkNHkvPGktzzXexsFw==", - "requires": { - "regenerator-runtime": "^0.14.0" - } - }, - "@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==", - "requires": { - "eslint-visitor-keys": "^3.3.0" - } - }, - "@eslint-community/regexpp": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", - "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==" - }, - "@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", - "requires": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "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" - }, - "dependencies": { - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "requires": { - "brace-expansion": "^1.1.7" - } - } - } - }, - "@eslint/js": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", - "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==" - }, "@gar/promisify": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", "dev": true }, - "@humanwhocodes/config-array": { - "version": "0.11.14", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", - "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", - "requires": { - "@humanwhocodes/object-schema": "^2.0.2", - "debug": "^4.3.1", - "minimatch": "^3.0.5" - } - }, - "@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==" - }, - "@humanwhocodes/object-schema": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", - "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==" - }, "@hutson/parse-repository-url": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz", @@ -11693,6 +8831,7 @@ "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, "requires": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" @@ -11701,12 +8840,14 @@ "@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==" + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true }, "@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, "requires": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" @@ -12353,11 +9494,6 @@ "dev": true, "optional": true }, - "@pkgr/core": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", - "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==" - }, "@sigstore/protobuf-specs": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.1.0.tgz", @@ -12423,16 +9559,6 @@ } } }, - "@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" - }, - "@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==" - }, "@types/minimatch": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz", @@ -12457,162 +9583,6 @@ "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", "dev": true }, - "@types/semver": { - "version": "7.5.8", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", - "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==" - }, - "@typescript-eslint/eslint-plugin": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.7.4.tgz", - "integrity": "sha512-DAbgDXwtX+pDkAHwiGhqP3zWUGpW49B7eqmgpPtg+BKJXwdct79ut9+ifqOFPJGClGKSHXn2PTBatCnldJRUoA==", - "requires": { - "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.7.4", - "@typescript-eslint/type-utils": "6.7.4", - "@typescript-eslint/utils": "6.7.4", - "@typescript-eslint/visitor-keys": "6.7.4", - "debug": "^4.3.4", - "graphemer": "^1.4.0", - "ignore": "^5.2.4", - "natural-compare": "^1.4.0", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "dependencies": { - "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==", - "requires": { - "yallist": "^4.0.0" - } - }, - "semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "@typescript-eslint/parser": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.7.4.tgz", - "integrity": "sha512-I5zVZFY+cw4IMZUeNCU7Sh2PO5O57F7Lr0uyhgCJmhN/BuTlnc55KxPonR4+EM3GBdfiCyGZye6DgMjtubQkmA==", - "requires": { - "@typescript-eslint/scope-manager": "6.7.4", - "@typescript-eslint/types": "6.7.4", - "@typescript-eslint/typescript-estree": "6.7.4", - "@typescript-eslint/visitor-keys": "6.7.4", - "debug": "^4.3.4" - } - }, - "@typescript-eslint/scope-manager": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.7.4.tgz", - "integrity": "sha512-SdGqSLUPTXAXi7c3Ob7peAGVnmMoGzZ361VswK2Mqf8UOYcODiYvs8rs5ILqEdfvX1lE7wEZbLyELCW+Yrql1A==", - "requires": { - "@typescript-eslint/types": "6.7.4", - "@typescript-eslint/visitor-keys": "6.7.4" - } - }, - "@typescript-eslint/type-utils": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.7.4.tgz", - "integrity": "sha512-n+g3zi1QzpcAdHFP9KQF+rEFxMb2KxtnJGID3teA/nxKHOVi3ylKovaqEzGBbVY2pBttU6z85gp0D00ufLzViQ==", - "requires": { - "@typescript-eslint/typescript-estree": "6.7.4", - "@typescript-eslint/utils": "6.7.4", - "debug": "^4.3.4", - "ts-api-utils": "^1.0.1" - } - }, - "@typescript-eslint/types": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.7.4.tgz", - "integrity": "sha512-o9XWK2FLW6eSS/0r/tgjAGsYasLAnOWg7hvZ/dGYSSNjCh+49k5ocPN8OmG5aZcSJ8pclSOyVKP2x03Sj+RrCA==" - }, - "@typescript-eslint/typescript-estree": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.4.tgz", - "integrity": "sha512-ty8b5qHKatlNYd9vmpHooQz3Vki3gG+3PchmtsA4TgrZBKWHNjWfkQid7K7xQogBqqc7/BhGazxMD5vr6Ha+iQ==", - "requires": { - "@typescript-eslint/types": "6.7.4", - "@typescript-eslint/visitor-keys": "6.7.4", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "dependencies": { - "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==", - "requires": { - "yallist": "^4.0.0" - } - }, - "semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "@typescript-eslint/utils": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.7.4.tgz", - "integrity": "sha512-PRQAs+HUn85Qdk+khAxsVV+oULy3VkbH3hQ8hxLRJXWBEd7iI+GbQxH5SEUSH7kbEoTp6oT1bOwyga24ELALTA==", - "requires": { - "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.7.4", - "@typescript-eslint/types": "6.7.4", - "@typescript-eslint/typescript-estree": "6.7.4", - "semver": "^7.5.4" - }, - "dependencies": { - "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==", - "requires": { - "yallist": "^4.0.0" - } - }, - "semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "@typescript-eslint/visitor-keys": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.4.tgz", - "integrity": "sha512-pOW37DUhlTZbvph50x5zZCkFn3xzwkGtNoJHzIM3svpiSkJzwOYr/kVBaXmf+RAQiUDs1AHEZVNPg6UJCJpwRA==", - "requires": { - "@typescript-eslint/types": "6.7.4", - "eslint-visitor-keys": "^3.4.1" - } - }, - "@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==" - }, "@yarnpkg/lockfile": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", @@ -12674,17 +9644,6 @@ "event-target-shim": "^5.0.0" } }, - "acorn": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", - "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==" - }, - "acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "requires": {} - }, "add-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/add-stream/-/add-stream-1.0.0.tgz", @@ -12721,17 +9680,6 @@ "indent-string": "^4.0.0" } }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "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-colors": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", @@ -12750,12 +9698,14 @@ "ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true }, "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": { "color-convert": "^2.0.1" } @@ -12779,24 +9729,8 @@ "argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, - "aria-query": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", - "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", - "requires": { - "dequal": "^2.0.3" - } - }, - "array-buffer-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", - "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", - "requires": { - "call-bind": "^1.0.5", - "is-array-buffer": "^3.0.4" - } + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true }, "array-differ": { "version": "3.0.0", @@ -12810,83 +9744,11 @@ "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==", "dev": true }, - "array-includes": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz", - "integrity": "sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", - "is-string": "^1.0.7" - } - }, "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==" - }, - "array.prototype.filter": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array.prototype.filter/-/array.prototype.filter-1.0.3.tgz", - "integrity": "sha512-VizNcj/RGJiUyQBgzwxzE5oHdeuXY5hSbbmKMlphj1cy1Vl7Pn2asCGbSrru6hSQjmCzqTBPVWAF/whmEOVHbw==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-array-method-boxes-properly": "^1.0.0", - "is-string": "^1.0.7" - } - }, - "array.prototype.findlastindex": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.4.tgz", - "integrity": "sha512-hzvSHUshSpCflDR1QMUBLHGHP1VIEBegT4pix9H/Z92Xw3ySoy6c2qh7lJWTJnRJ8JCZ9bJNCgTyYaJGcJu6xQ==", - "requires": { - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.3.0", - "es-shim-unscopables": "^1.0.2" - } - }, - "array.prototype.flat": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", - "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - } - }, - "array.prototype.flatmap": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", - "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - } - }, - "arraybuffer.prototype.slice": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", - "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", - "requires": { - "array-buffer-byte-length": "^1.0.1", - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.2.1", - "get-intrinsic": "^1.2.3", - "is-array-buffer": "^3.0.4", - "is-shared-array-buffer": "^1.0.2" - } + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true }, "arrify": { "version": "1.0.1", @@ -12894,25 +9756,12 @@ "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", "dev": true }, - "ast-types-flow": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", - "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==" - }, "async": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", "dev": true }, - "asynciterator.prototype": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/asynciterator.prototype/-/asynciterator.prototype-1.0.0.tgz", - "integrity": "sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg==", - "requires": { - "has-symbols": "^1.0.3" - } - }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -12925,19 +9774,6 @@ "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", "dev": true }, - "available-typed-arrays": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", - "requires": { - "possible-typed-array-names": "^1.0.0" - } - }, - "axe-core": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.7.0.tgz", - "integrity": "sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ==" - }, "axios": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz", @@ -12949,18 +9785,11 @@ "proxy-from-env": "^1.1.0" } }, - "axobject-query": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", - "integrity": "sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==", - "requires": { - "dequal": "^2.0.3" - } - }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true }, "base64-js": { "version": "1.5.1", @@ -13031,6 +9860,7 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -13040,6 +9870,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, "requires": { "fill-range": "^7.0.1" } @@ -13060,16 +9891,11 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, - "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==", - "peer": true - }, "builtins": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==", + "dev": true, "requires": { "semver": "^7.0.0" } @@ -13111,22 +9937,11 @@ } } }, - "call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", - "requires": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" - } - }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true }, "camelcase": { "version": "5.3.1", @@ -13149,6 +9964,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -13251,6 +10067,7 @@ "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, "requires": { "color-name": "~1.1.4" } @@ -13258,7 +10075,8 @@ "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==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "color-support": { "version": "1.1.3", @@ -13315,7 +10133,8 @@ "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true }, "concat-stream": { "version": "2.0.0", @@ -13503,6 +10322,7 @@ "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, "requires": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -13513,6 +10333,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, "requires": { "isexe": "^2.0.0" } @@ -13531,11 +10352,6 @@ "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", "dev": true }, - "damerau-levenshtein": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", - "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==" - }, "dargs": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/dargs/-/dargs-7.0.0.tgz", @@ -13552,6 +10368,7 @@ "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, "requires": { "ms": "2.1.2" } @@ -13586,11 +10403,6 @@ "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", "dev": true }, - "deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" - }, "defaults": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", @@ -13600,32 +10412,12 @@ "clone": "^1.0.2" } }, - "define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "requires": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - } - }, "define-lazy-prop": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", "dev": true }, - "define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "requires": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - } - }, "del": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/del/-/del-6.1.1.tgz", @@ -13700,11 +10492,6 @@ "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", "dev": true }, - "dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==" - }, "detect-indent": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-5.0.0.tgz", @@ -13715,16 +10502,9 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, "requires": { - "path-type": "^4.0.0" - } - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "requires": { - "esutils": "^2.0.2" + "path-type": "^4.0.0" } }, "dot-prop": { @@ -13773,671 +10553,80 @@ "version": "0.1.13", "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", - "dev": true, - "optional": true, - "requires": { - "iconv-lite": "^0.6.2" - }, - "dependencies": { - "iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, - "optional": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - } - } - } - }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, - "requires": { - "once": "^1.4.0" - } - }, - "enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "requires": { - "ansi-colors": "^4.1.1" - } - }, - "env-paths": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", - "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", - "dev": true - }, - "envinfo": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", - "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==", - "dev": true - }, - "err-code": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", - "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", - "dev": true - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "es-abstract": { - "version": "1.22.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.5.tgz", - "integrity": "sha512-oW69R+4q2wG+Hc3KZePPZxOiisRIqfKBVo/HLx94QcJeWGU/8sZhCvc829rd1kS366vlJbzBfXf9yWwf0+Ko7w==", - "requires": { - "array-buffer-byte-length": "^1.0.1", - "arraybuffer.prototype.slice": "^1.0.3", - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "es-set-tostringtag": "^2.0.3", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.4", - "get-symbol-description": "^1.0.2", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.3", - "has-symbols": "^1.0.3", - "hasown": "^2.0.1", - "internal-slot": "^1.0.7", - "is-array-buffer": "^3.0.4", - "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.3", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.3", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.13", - "is-weakref": "^1.0.2", - "object-inspect": "^1.13.1", - "object-keys": "^1.1.1", - "object.assign": "^4.1.5", - "regexp.prototype.flags": "^1.5.2", - "safe-array-concat": "^1.1.0", - "safe-regex-test": "^1.0.3", - "string.prototype.trim": "^1.2.8", - "string.prototype.trimend": "^1.0.7", - "string.prototype.trimstart": "^1.0.7", - "typed-array-buffer": "^1.0.2", - "typed-array-byte-length": "^1.0.1", - "typed-array-byte-offset": "^1.0.2", - "typed-array-length": "^1.0.5", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.14" - } - }, - "es-array-method-boxes-properly": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", - "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==" - }, - "es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", - "requires": { - "get-intrinsic": "^1.2.4" - } - }, - "es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==" - }, - "es-iterator-helpers": { - "version": "1.0.17", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.17.tgz", - "integrity": "sha512-lh7BsUqelv4KUbR5a/ZTaGGIMLCjPGPqJ6q+Oq24YP0RdyptX1uzm4vvaqzk7Zx3bpl/76YLTTDj9L7uYQ92oQ==", - "requires": { - "asynciterator.prototype": "^1.0.0", - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.22.4", - "es-errors": "^1.3.0", - "es-set-tostringtag": "^2.0.2", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "globalthis": "^1.0.3", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.7", - "iterator.prototype": "^1.1.2", - "safe-array-concat": "^1.1.0" - } - }, - "es-set-tostringtag": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", - "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", - "requires": { - "get-intrinsic": "^1.2.4", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.1" - } - }, - "es-shim-unscopables": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", - "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", - "requires": { - "hasown": "^2.0.0" - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true - }, - "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 - }, - "eslint": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", - "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", - "requires": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.0", - "@humanwhocodes/config-array": "^0.11.14", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@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.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", - "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", - "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.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "dependencies": { - "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==" - }, - "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==", - "requires": { - "is-glob": "^4.0.3" - } - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "requires": { - "brace-expansion": "^1.1.7" - } - } - } - }, - "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==", - "peer": true, - "requires": {} - }, - "eslint-config-prettier": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", - "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", - "requires": {} - }, - "eslint-config-standard": { - "version": "17.1.0", - "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-17.1.0.tgz", - "integrity": "sha512-IwHwmaBNtDK4zDHQukFDW5u/aTb8+meQWZvNFWkiGmbWjD6bqyuSSBxxXKkCftCUzc1zwCH2m/baCNDLGmuO5Q==", - "requires": {} - }, - "eslint-config-standard-with-typescript": { - "version": "39.1.1", - "resolved": "https://registry.npmjs.org/eslint-config-standard-with-typescript/-/eslint-config-standard-with-typescript-39.1.1.tgz", - "integrity": "sha512-t6B5Ep8E4I18uuoYeYxINyqcXb2UbC0SOOTxRtBSt2JUs+EzeXbfe2oaiPs71AIdnoWhXDO2fYOHz8df3kV84A==", - "requires": { - "@typescript-eslint/parser": "^6.4.0", - "eslint-config-standard": "17.1.0" - } - }, - "eslint-import-resolver-node": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", - "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", - "requires": { - "debug": "^3.2.7", - "is-core-module": "^2.13.0", - "resolve": "^1.22.4" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "requires": { - "ms": "^2.1.1" - } - } - } - }, - "eslint-module-utils": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.1.tgz", - "integrity": "sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==", - "requires": { - "debug": "^3.2.7" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "requires": { - "ms": "^2.1.1" - } - } - } - }, - "eslint-plugin-es": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", - "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", - "requires": { - "eslint-utils": "^2.0.0", - "regexpp": "^3.0.0" - } - }, - "eslint-plugin-es-x": { - "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==", - "peer": true, - "requires": { - "@eslint-community/eslint-utils": "^4.1.2", - "@eslint-community/regexpp": "^4.6.0", - "eslint-compat-utils": "^0.1.2" - } - }, - "eslint-plugin-import": { - "version": "2.29.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz", - "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==", - "requires": { - "array-includes": "^3.1.7", - "array.prototype.findlastindex": "^1.2.3", - "array.prototype.flat": "^1.3.2", - "array.prototype.flatmap": "^1.3.2", - "debug": "^3.2.7", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.9", - "eslint-module-utils": "^2.8.0", - "hasown": "^2.0.0", - "is-core-module": "^2.13.1", - "is-glob": "^4.0.3", - "minimatch": "^3.1.2", - "object.fromentries": "^2.0.7", - "object.groupby": "^1.0.1", - "object.values": "^1.1.7", - "semver": "^6.3.1", - "tsconfig-paths": "^3.15.0" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "requires": { - "ms": "^2.1.1" - } - }, - "doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "requires": { - "esutils": "^2.0.2" - } - }, - "json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "requires": { - "minimist": "^1.2.0" - } - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==" - }, - "tsconfig-paths": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", - "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", - "requires": { - "@types/json5": "^0.0.29", - "json5": "^1.0.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - } - } - } - }, - "eslint-plugin-jest": { - "version": "27.9.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-27.9.0.tgz", - "integrity": "sha512-QIT7FH7fNmd9n4se7FFKHbsLKGQiw885Ds6Y/sxKgCZ6natwCsXdgPOADnYVxN2QrRweF0FZWbJ6S7Rsn7llug==", - "requires": { - "@typescript-eslint/utils": "^5.10.0" - }, - "dependencies": { - "@typescript-eslint/scope-manager": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", - "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", - "requires": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0" - } - }, - "@typescript-eslint/types": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", - "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==" - }, - "@typescript-eslint/typescript-estree": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", - "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", - "requires": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/utils": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", - "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", - "requires": { - "@eslint-community/eslint-utils": "^4.2.0", - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/typescript-estree": "5.62.0", - "eslint-scope": "^5.1.1", - "semver": "^7.3.7" - } - }, - "@typescript-eslint/visitor-keys": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", - "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", - "requires": { - "@typescript-eslint/types": "5.62.0", - "eslint-visitor-keys": "^3.3.0" - } - }, - "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" - } - } - }, - "eslint-plugin-jsx-a11y": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.8.0.tgz", - "integrity": "sha512-Hdh937BS3KdwwbBaKd5+PLCOmYY6U4f2h9Z2ktwtNKvIdIEu137rjYbcb9ApSbVJfWxANNuiKTD/9tOKjK9qOA==", - "requires": { - "@babel/runtime": "^7.23.2", - "aria-query": "^5.3.0", - "array-includes": "^3.1.7", - "array.prototype.flatmap": "^1.3.2", - "ast-types-flow": "^0.0.8", - "axe-core": "=4.7.0", - "axobject-query": "^3.2.1", - "damerau-levenshtein": "^1.0.8", - "emoji-regex": "^9.2.2", - "es-iterator-helpers": "^1.0.15", - "hasown": "^2.0.0", - "jsx-ast-utils": "^3.3.5", - "language-tags": "^1.0.9", - "minimatch": "^3.1.2", - "object.entries": "^1.1.7", - "object.fromentries": "^2.0.7" - }, - "dependencies": { - "emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "requires": { - "brace-expansion": "^1.1.7" - } - } - } - }, - "eslint-plugin-n": { - "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==", - "peer": true, - "requires": { - "@eslint-community/eslint-utils": "^4.4.0", - "builtins": "^5.0.1", - "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.3" - }, - "dependencies": { - "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==", - "peer": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "peer": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "peer": true, + "dev": true, + "optional": true, + "requires": { + "iconv-lite": "^0.6.2" + }, + "dependencies": { + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "optional": true, "requires": { - "lru-cache": "^6.0.0" + "safer-buffer": ">= 2.1.2 < 3.0.0" } } } }, - "eslint-plugin-node": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", - "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, "requires": { - "eslint-plugin-es": "^3.0.0", - "eslint-utils": "^2.0.0", - "ignore": "^5.1.1", - "minimatch": "^3.0.4", - "resolve": "^1.10.1", - "semver": "^6.1.0" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" - } + "once": "^1.4.0" } }, - "eslint-plugin-prettier": { - "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==", + "enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, "requires": { - "prettier-linter-helpers": "^1.0.0", - "synckit": "^0.8.6" + "ansi-colors": "^4.1.1" } }, - "eslint-plugin-promise": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.1.1.tgz", - "integrity": "sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig==", - "requires": {} + "env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true }, - "eslint-plugin-react-hooks": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", - "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", - "requires": {} + "envinfo": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", + "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==", + "dev": true }, - "eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - } + "err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "dev": true }, - "eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, "requires": { - "eslint-visitor-keys": "^1.1.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==" - } + "is-arrayish": "^0.2.1" } }, - "eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==" + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true }, - "espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "requires": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - } + "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 }, "esprima": { "version": "4.0.1", @@ -14445,32 +10634,6 @@ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true }, - "esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", - "requires": { - "estraverse": "^5.1.0" - } - }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "requires": { - "estraverse": "^5.2.0" - } - }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" - }, "event-target-shim": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", @@ -14534,20 +10697,11 @@ } } }, - "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==" - }, - "fast-diff": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", - "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==" - }, "fast-glob": { "version": "3.2.12", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -14556,20 +10710,11 @@ "micromatch": "^4.0.4" } }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" - }, "fastq": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, "requires": { "reusify": "^1.0.4" } @@ -14583,14 +10728,6 @@ "escape-string-regexp": "^1.0.5" } }, - "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==", - "requires": { - "flat-cache": "^3.0.4" - } - }, "file-url": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/file-url/-/file-url-3.0.0.tgz", @@ -14630,6 +10767,7 @@ "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, "requires": { "to-regex-range": "^5.0.1" } @@ -14638,6 +10776,7 @@ "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, "requires": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -14649,66 +10788,12 @@ "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", "dev": true }, - "flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", - "requires": { - "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" - }, - "dependencies": { - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "requires": { - "glob": "^7.1.3" - } - } - } - }, - "flatted": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", - "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==" - }, "follow-redirects": { "version": "1.15.2", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", "dev": true }, - "for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "requires": { - "is-callable": "^1.1.3" - } - }, "foreground-child": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", @@ -14768,28 +10853,14 @@ "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true }, "function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" - }, - "function.prototype.name": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", - "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "functions-have-names": "^1.2.3" - } - }, - "functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==" + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true }, "gauge": { "version": "4.0.4", @@ -14813,18 +10884,6 @@ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, - "get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", - "requires": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" - } - }, "get-pkg-repo": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/get-pkg-repo/-/get-pkg-repo-4.2.1.tgz", @@ -14909,25 +10968,6 @@ "integrity": "sha512-A1B3Bh1UmL0bidM/YX2NsCOTnGJePL9rO/M+Mw3m9f2gUpfokS0hi5Eah0WSUEWZdZhIZtMjkIYS7mDfOqNHbg==", "dev": true }, - "get-symbol-description": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", - "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", - "requires": { - "call-bind": "^1.0.5", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4" - } - }, - "get-tsconfig": { - "version": "4.7.3", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.3.tgz", - "integrity": "sha512-ZvkrzoUA0PQZM6fy6+/Hce561s+faD1rsNwhnO5FelNjyy7EMGJ3Rz1AQ8GYDWjhRs/7dBLOEJvhK8MiEJOAFg==", - "peer": true, - "requires": { - "resolve-pkg-maps": "^1.0.0" - } - }, "git-raw-commits": { "version": "2.0.11", "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.11.tgz", @@ -15042,37 +11082,16 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, "requires": { "is-glob": "^4.0.1" } }, - "globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "requires": { - "type-fest": "^0.20.2" - }, - "dependencies": { - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==" - } - } - }, - "globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", - "requires": { - "define-properties": "^1.1.3" - } - }, "globby": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, "requires": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", @@ -15082,25 +11101,12 @@ "slash": "^3.0.0" } }, - "gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "requires": { - "get-intrinsic": "^1.1.3" - } - }, "graceful-fs": { "version": "4.2.10", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", "dev": true }, - "graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==" - }, "handlebars": { "version": "4.7.7", "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", @@ -15120,41 +11126,11 @@ "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", "dev": true }, - "has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==" - }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "requires": { - "es-define-property": "^1.0.0" - } - }, - "has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==" - }, - "has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" - }, - "has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "requires": { - "has-symbols": "^1.0.3" - } + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true }, "has-unicode": { "version": "2.0.1", @@ -15166,6 +11142,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz", "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==", + "dev": true, "requires": { "function-bind": "^1.1.2" } @@ -15245,7 +11222,8 @@ "ignore": { "version": "5.2.4", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==" + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true }, "ignore-walk": { "version": "5.0.1", @@ -15280,6 +11258,7 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, "requires": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -15288,7 +11267,8 @@ "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==" + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true } } }, @@ -15305,7 +11285,8 @@ "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==" + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true }, "indent-string": { "version": "4.0.0", @@ -15323,6 +11304,7 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, "requires": { "once": "^1.3.0", "wrappy": "1" @@ -15331,7 +11313,8 @@ "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true }, "ini": { "version": "1.3.8", @@ -15418,76 +11401,18 @@ } } }, - "internal-slot": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", - "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", - "requires": { - "es-errors": "^1.3.0", - "hasown": "^2.0.0", - "side-channel": "^1.0.4" - } - }, "ip": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==", "dev": true }, - "is-array-buffer": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", - "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1" - } - }, "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "dev": true }, - "is-async-function": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", - "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "requires": { - "has-bigints": "^1.0.1" - } - }, - "is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "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==", - "peer": true, - "requires": { - "builtin-modules": "^3.3.0" - } - }, - "is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==" - }, "is-ci": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", @@ -15501,18 +11426,11 @@ "version": "2.13.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dev": true, "requires": { "hasown": "^2.0.0" } }, - "is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "requires": { - "has-tostringtag": "^1.0.0" - } - }, "is-docker": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", @@ -15522,15 +11440,8 @@ "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" - }, - "is-finalizationregistry": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz", - "integrity": "sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==", - "requires": { - "call-bind": "^1.0.2" - } + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true }, "is-fullwidth-code-point": { "version": "3.0.0", @@ -15538,18 +11449,11 @@ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true }, - "is-generator-function": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", - "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", - "requires": { - "has-tostringtag": "^1.0.0" - } - }, "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, "requires": { "is-extglob": "^2.1.1" } @@ -15566,28 +11470,11 @@ "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", "dev": true }, - "is-map": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", - "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==" - }, - "is-negative-zero": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", - "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==" - }, "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" - }, - "is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", - "requires": { - "has-tostringtag": "^1.0.0" - } + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true }, "is-obj": { "version": "2.0.0", @@ -15604,7 +11491,8 @@ "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==" + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true }, "is-plain-obj": { "version": "1.1.0", @@ -15618,28 +11506,6 @@ "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", "dev": true }, - "is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-set": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", - "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==" - }, - "is-shared-array-buffer": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", - "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", - "requires": { - "call-bind": "^1.0.7" - } - }, "is-ssh": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.4.0.tgz", @@ -15655,22 +11521,6 @@ "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", "dev": true }, - "is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "requires": { - "has-symbols": "^1.0.2" - } - }, "is-text-path": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz", @@ -15680,42 +11530,12 @@ "text-extensions": "^1.0.0" } }, - "is-typed-array": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", - "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", - "requires": { - "which-typed-array": "^1.1.14" - } - }, "is-unicode-supported": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true }, - "is-weakmap": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", - "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==" - }, - "is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "requires": { - "call-bind": "^1.0.2" - } - }, - "is-weakset": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.3.tgz", - "integrity": "sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==", - "requires": { - "call-bind": "^1.0.7", - "get-intrinsic": "^1.2.4" - } - }, "is-wsl": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", @@ -15734,7 +11554,8 @@ "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true }, "isobject": { "version": "3.0.1", @@ -15742,18 +11563,6 @@ "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", "dev": true }, - "iterator.prototype": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz", - "integrity": "sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==", - "requires": { - "define-properties": "^1.2.1", - "get-intrinsic": "^1.2.1", - "has-symbols": "^1.0.3", - "reflect.getprototypeof": "^1.0.4", - "set-function-name": "^2.0.1" - } - }, "jackspeak": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.2.1.tgz", @@ -15797,15 +11606,11 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, "requires": { "argparse": "^2.0.1" } }, - "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==" - }, "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", @@ -15818,16 +11623,6 @@ "integrity": "sha512-iZbGHafX/59r39gPwVPRBGw0QQKnA7tte5pSMrhWOW7swGsVvVTjmfyAV9pNqk8YGT7tRCdxRu8uzcgZwoDooA==", "dev": 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==" - }, - "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==" - }, "json-stringify-nice": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/json-stringify-nice/-/json-stringify-nice-1.1.4.tgz", @@ -15878,17 +11673,6 @@ "through": ">=2.2.7 <3" } }, - "jsx-ast-utils": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", - "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", - "requires": { - "array-includes": "^3.1.6", - "array.prototype.flat": "^1.3.1", - "object.assign": "^4.1.4", - "object.values": "^1.1.6" - } - }, "just-diff": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/just-diff/-/just-diff-6.0.2.tgz", @@ -15901,33 +11685,12 @@ "integrity": "sha512-OYTthRfSh55WOItVqwpefPtNt2VdKsq5AnAK6apdtR6yCH8pr0CmSr710J0Mf+WdQy7K/OzMy7K2MgAfdQURDw==", "dev": true }, - "keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "requires": { - "json-buffer": "3.0.1" - } - }, "kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true }, - "language-subtag-registry": { - "version": "0.3.22", - "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz", - "integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==" - }, - "language-tags": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", - "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", - "requires": { - "language-subtag-registry": "^0.3.20" - } - }, "lerna": { "version": "6.6.2", "resolved": "https://registry.npmjs.org/lerna/-/lerna-6.6.2.tgz", @@ -16012,15 +11775,6 @@ "yargs-parser": "20.2.4" } }, - "levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - } - }, "libnpmaccess": { "version": "6.0.4", "resolved": "https://registry.npmjs.org/libnpmaccess/-/libnpmaccess-6.0.4.tgz", @@ -16344,6 +12098,7 @@ "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, "requires": { "p-locate": "^5.0.0" } @@ -16360,11 +12115,6 @@ "integrity": "sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g==", "dev": true }, - "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==" - }, "log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -16604,12 +12354,14 @@ "merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true }, "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": { "braces": "^3.0.2", "picomatch": "^2.3.1" @@ -16646,6 +12398,7 @@ "version": "3.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.5.tgz", "integrity": "sha512-tUpxzX0VAzJHjLu0xUfFv1gwVp9ba3IOuRAVH2EGuRW8a5emA2FlACLqiT/lDVtS1W+TGNwqz3sWaNyLgDJWuw==", + "dev": true, "requires": { "brace-expansion": "^1.1.7" } @@ -16653,7 +12406,8 @@ "minimist": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true }, "minimist-options": { "version": "4.1.0", @@ -16832,7 +12586,8 @@ "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true }, "multimatch": { "version": "5.0.0", @@ -16861,11 +12616,6 @@ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "dev": true }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" - }, "negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -17365,73 +13115,11 @@ } } }, - "object-inspect": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", - "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==" - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" - }, - "object.assign": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", - "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", - "requires": { - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - } - }, - "object.entries": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.7.tgz", - "integrity": "sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - } - }, - "object.fromentries": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz", - "integrity": "sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - } - }, - "object.groupby": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.2.tgz", - "integrity": "sha512-bzBq58S+x+uo0VjurFT0UktpKHOZmv4/xePiOA1nbB9pMqpGK7rUPNgf+1YC+7mE+0HzhTMqNUuCqvKhj6FnBw==", - "requires": { - "array.prototype.filter": "^1.0.3", - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.0.0" - } - }, - "object.values": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz", - "integrity": "sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - } - }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, "requires": { "wrappy": "1" } @@ -17456,19 +13144,6 @@ "is-wsl": "^2.2.0" } }, - "optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", - "requires": { - "@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" - } - }, "ora": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", @@ -17502,6 +13177,7 @@ "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": { "yocto-queue": "^0.1.0" } @@ -17510,6 +13186,7 @@ "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": { "p-limit": "^3.0.2" } @@ -17712,6 +13389,7 @@ "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, "requires": { "callsites": "^3.0.0" } @@ -17774,22 +13452,26 @@ "path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true }, "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==" + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true }, "path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true }, "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==" + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true }, "path-scurry": { "version": "1.9.2", @@ -17812,12 +13494,14 @@ "path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true }, "picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true }, "pify": { "version": "5.0.0", @@ -17873,11 +13557,6 @@ } } }, - "possible-typed-array-names": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", - "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==" - }, "postcss-selector-parser": { "version": "6.0.13", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz", @@ -17888,24 +13567,6 @@ "util-deprecate": "^1.0.2" } }, - "prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==" - }, - "prettier": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", - "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==" - }, - "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==", - "requires": { - "fast-diff": "^1.1.2" - } - }, "pretty-format": { "version": "29.4.3", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.4.3.tgz", @@ -17998,11 +13659,6 @@ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "dev": true }, - "punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==" - }, "q": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", @@ -18012,7 +13668,8 @@ "queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true }, "quick-lru": { "version": "4.0.1", @@ -18291,41 +13948,6 @@ "strip-indent": "^3.0.0" } }, - "reflect.getprototypeof": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.5.tgz", - "integrity": "sha512-62wgfC8dJWrmxv44CA36pLDnP6KKl3Vhxb7PL+8+qrrFMMoJij4vgiMP8zV4O8+CBMXY1mHxI5fITGHXFHVmQQ==", - "requires": { - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.0.0", - "get-intrinsic": "^1.2.3", - "globalthis": "^1.0.3", - "which-builtin-type": "^1.1.3" - } - }, - "regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" - }, - "regexp.prototype.flags": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", - "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", - "requires": { - "call-bind": "^1.0.6", - "define-properties": "^1.2.1", - "es-errors": "^1.3.0", - "set-function-name": "^2.0.1" - } - }, - "regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==" - }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -18336,6 +13958,7 @@ "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, "requires": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", @@ -18357,12 +13980,6 @@ "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true }, - "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==", - "peer": true - }, "restore-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", @@ -18382,7 +13999,8 @@ "reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true }, "rimraf": { "version": "4.4.1", @@ -18441,6 +14059,7 @@ "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": { "queue-microtask": "^1.2.2" } @@ -18454,40 +14073,12 @@ "tslib": "^2.1.0" } }, - "safe-array-concat": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", - "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", - "requires": { - "call-bind": "^1.0.7", - "get-intrinsic": "^1.2.4", - "has-symbols": "^1.0.3", - "isarray": "^2.0.5" - }, - "dependencies": { - "isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" - } - } - }, "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==", "dev": true }, - "safe-regex-test": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", - "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", - "requires": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-regex": "^1.1.4" - } - }, "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -18498,6 +14089,7 @@ "version": "7.5.2", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.2.tgz", "integrity": "sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ==", + "dev": true, "requires": { "lru-cache": "^6.0.0" }, @@ -18506,6 +14098,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, "requires": { "yallist": "^4.0.0" } @@ -18518,30 +14111,6 @@ "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "dev": true }, - "set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "requires": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - } - }, - "set-function-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", - "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", - "requires": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.2" - } - }, "shallow-clone": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", @@ -18555,6 +14124,7 @@ "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": { "shebang-regex": "^3.0.0" } @@ -18562,18 +14132,8 @@ "shebang-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" - }, - "side-channel": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", - "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", - "requires": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" - } + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true }, "signal-exit": { "version": "3.0.7", @@ -18596,7 +14156,8 @@ "slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true }, "smart-buffer": { "version": "4.2.0", @@ -18747,40 +14308,11 @@ "strip-ansi": "^6.0.1" } }, - "string.prototype.trim": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", - "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - } - }, - "string.prototype.trimend": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", - "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - } - }, - "string.prototype.trimstart": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", - "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - } - }, "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": { "ansi-regex": "^5.0.1" } @@ -18815,11 +14347,6 @@ "min-indent": "^1.0.0" } }, - "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==" - }, "strong-log-transformer": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/strong-log-transformer/-/strong-log-transformer-2.1.0.tgz", @@ -18835,6 +14362,7 @@ "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": { "has-flag": "^4.0.0" } @@ -18842,16 +14370,8 @@ "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==" - }, - "synckit": { - "version": "0.8.8", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.8.tgz", - "integrity": "sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==", - "requires": { - "@pkgr/core": "^0.1.0", - "tslib": "^2.6.2" - } + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true }, "tar": { "version": "6.1.11", @@ -18939,11 +14459,6 @@ "integrity": "sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==", "dev": true }, - "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==" - }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -19006,6 +14521,7 @@ "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": { "is-number": "^7.0.0" } @@ -19028,12 +14544,6 @@ "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", "dev": true }, - "ts-api-utils": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", - "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", - "requires": {} - }, "tsconfig-paths": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", @@ -19056,22 +14566,8 @@ "tslib": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" - }, - "tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "requires": { - "tslib": "^1.8.1" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - } - } + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true }, "tuf-js": { "version": "1.1.7", @@ -19084,68 +14580,12 @@ "make-fetch-happen": "^11.1.1" } }, - "type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "requires": { - "prelude-ls": "^1.2.1" - } - }, "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 }, - "typed-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", - "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", - "requires": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "is-typed-array": "^1.1.13" - } - }, - "typed-array-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", - "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", - "requires": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" - } - }, - "typed-array-byte-offset": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", - "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", - "requires": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" - } - }, - "typed-array-length": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.5.tgz", - "integrity": "sha512-yMi0PlwuznKHxKmcpoOdeLwxBoVPkqZxd7q2FgMkmD3bNwvF5VW0+UlUQ1k1vmktTu4Yu13Q0RIxEP8+B+wloA==", - "requires": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13", - "possible-typed-array-names": "^1.0.0" - } - }, "typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", @@ -19155,7 +14595,8 @@ "typescript": { "version": "4.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz", - "integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==" + "integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==", + "dev": true }, "uglify-js": { "version": "3.17.4", @@ -19164,17 +14605,6 @@ "dev": true, "optional": true }, - "unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "requires": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - } - }, "unique-filename": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz", @@ -19220,14 +14650,6 @@ "integrity": "sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w==", "dev": true }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "requires": { - "punycode": "^2.1.0" - } - }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -19305,67 +14727,6 @@ "isexe": "^2.0.0" } }, - "which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "requires": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - } - }, - "which-builtin-type": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.3.tgz", - "integrity": "sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==", - "requires": { - "function.prototype.name": "^1.1.5", - "has-tostringtag": "^1.0.0", - "is-async-function": "^2.0.0", - "is-date-object": "^1.0.5", - "is-finalizationregistry": "^1.0.2", - "is-generator-function": "^1.0.10", - "is-regex": "^1.1.4", - "is-weakref": "^1.0.2", - "isarray": "^2.0.5", - "which-boxed-primitive": "^1.0.2", - "which-collection": "^1.0.1", - "which-typed-array": "^1.1.9" - }, - "dependencies": { - "isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" - } - } - }, - "which-collection": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", - "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", - "requires": { - "is-map": "^2.0.3", - "is-set": "^2.0.3", - "is-weakmap": "^2.0.2", - "is-weakset": "^2.0.3" - } - }, - "which-typed-array": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", - "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", - "requires": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.2" - } - }, "wide-align": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", @@ -19406,7 +14767,8 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true }, "write-file-atomic": { "version": "4.0.1", @@ -19501,7 +14863,8 @@ "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true }, "yaml": { "version": "1.10.2", @@ -19533,7 +14896,8 @@ "yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true } } } diff --git a/packages/desktop/CHANGELOG.md b/packages/desktop/CHANGELOG.md index dc3bd2fff0..a3a0c229f7 100644 --- a/packages/desktop/CHANGELOG.md +++ b/packages/desktop/CHANGELOG.md @@ -1,3 +1,37 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +# [2.2.0-alpha.0](/compare/@quiet/desktop@2.0.3-alpha.15...@quiet/desktop@2.2.0-alpha.0) (2024-04-04) + + +### Bug Fixes + +* "Invite A Friend" tab now reads "Add Members" (#2234) 049cd56, closes #2234 +* Add `patch` to requirements documentation (#1766) 8eb6fcc, closes #1766 +* Allow JPEG and GIF images as profile photos #2332 (#2353) 233725f, closes #2332 #2353 +* Clean up desktop UI console errors/warnings (#2226) 570a7a9, closes #2226 +* cleanup username creation component (#2216) 1d03995, closes #2216 +* create jdenticon from pubKey, not username - to distinguish user… (#2207) fd8bd06, closes #2207 +* Enable channel context menu for all users (#2206) 02f6809, closes #2206 +* Make community name field text visible on create community page (#2233) 0f4a33f, closes #2233 +* make sure local peer's address in in invitation link (#2268) 53f1ec9, closes #2268 +* pasting multiple files #1987 (#2306) 7c6b669, closes #1987 #2306 +* Quick fix for intro message race condition (#2376) d4f7449, closes #2376 +* Remove duplicate introduction messages once again (#2296) 655a812, closes #2296 +* Updating channel naming logic (#2307) 38b007e, closes #2307 + + +### Features + +* Add user profile feature for desktop (#1923) d016be5, closes #1923 +* Feat/519 big emoji messages (#2389) 71c8b22, closes #2389 + + + + + [unreleased] diff --git a/packages/desktop/package-lock.json b/packages/desktop/package-lock.json index 6389d13e85..4294790b37 100644 --- a/packages/desktop/package-lock.json +++ b/packages/desktop/package-lock.json @@ -1,12 +1,12 @@ { "name": "@quiet/desktop", - "version": "2.1.2", + "version": "2.2.0-alpha.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@quiet/desktop", - "version": "2.1.2", + "version": "2.2.0-alpha.0", "license": "GPL-3.0-or-later", "dependencies": { "@electron/remote": "^2.0.8", diff --git a/packages/desktop/package.json b/packages/desktop/package.json index 5e4df8c2c7..0784f8c19a 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -80,7 +80,7 @@ }, "homepage": "https://github.com/TryQuiet", "@comment version": "To build new version for specific platform, just replace platform in version tag to one of following linux, mac, windows", - "version": "2.1.2", + "version": "2.2.0-alpha.0", "description": "Decentralized team chat", "main": "dist/main/main.js", "scripts": { diff --git a/packages/mobile/CHANGELOG.md b/packages/mobile/CHANGELOG.md index dc3bd2fff0..61c9d5037b 100644 --- a/packages/mobile/CHANGELOG.md +++ b/packages/mobile/CHANGELOG.md @@ -1,3 +1,35 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +# [2.2.0-alpha.0](/compare/@quiet/mobile@2.0.3-alpha.17...@quiet/mobile@2.2.0-alpha.0) (2024-04-04) + + +### Bug Fixes + +* Add `patch` to requirements documentation (#1766) 8eb6fcc, closes #1766 +* Add retry ability to tor-control and update Tor port on resume (#2360) 9517f08, closes #2360 +* ask push notification runtime permission on Android (#2213) 2f92e88, closes #2213 +* calling init websocket connection (#2261) fe1d9dd, closes #2261 +* cleanup username creation component (#2216) 1d03995, closes #2216 +* create jdenticon from pubKey, not username - to distinguish user… (#2207) fd8bd06, closes #2207 +* deep linking issues (#2154) 2867264, closes #2154 #1970 +* delay node start (#2300) 810f7c3, closes #2300 +* make sure local peer's address in in invitation link (#2268) 53f1ec9, closes #2268 +* Reduce max random port on iOS (#2402) 2c783aa, closes #2402 +* Updating channel naming logic (#2307) 38b007e, closes #2307 +* Use useLegacyPackaging feature in Gradle (#2384) 125ec4e, closes #2384 + + +### Features + +* Add user profile feature for desktop (#1923) d016be5, closes #1923 + + + + + [unreleased] diff --git a/packages/mobile/android/app/build.gradle b/packages/mobile/android/app/build.gradle index fa597cfbf1..021ad4583c 100644 --- a/packages/mobile/android/app/build.gradle +++ b/packages/mobile/android/app/build.gradle @@ -192,8 +192,8 @@ android { applicationId "com.quietmobile" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 411 - versionName "2.1.2" + versionCode 412 + versionName "2.2.0-alpha.0" resValue "string", "build_config_package", "com.quietmobile" testBuildType System.getProperty('testBuildType', 'debug') testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' diff --git a/packages/mobile/ios/Quiet/Info.plist b/packages/mobile/ios/Quiet/Info.plist index 8e729a9b48..1140a0beac 100644 --- a/packages/mobile/ios/Quiet/Info.plist +++ b/packages/mobile/ios/Quiet/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 2.1.2 + 2.2.0 CFBundleSignature ???? CFBundleURLTypes @@ -34,28 +34,28 @@ CFBundleVersion - 368 + 369 ITSAppUsesNonExemptEncryption - + LSRequiresIPhoneOS - + NSAppTransportSecurity NSAllowsArbitraryLoads - + NSAllowsLocalNetworking - + NSExceptionDomains localhost NSExceptionAllowsInsecureHTTPLoads - + NSLocationWhenInUseUsageDescription - + UIAppFonts Rubik-Black.ttf @@ -74,7 +74,7 @@ Rubik-SemiBoldItalic.ttf UIBackgroundModes - + UILaunchStoryboardName LaunchScreen UIRequiredDeviceCapabilities @@ -88,6 +88,6 @@ UIInterfaceOrientationLandscapeRight UIViewControllerBasedStatusBarAppearance - + diff --git a/packages/mobile/ios/QuietTests/Info.plist b/packages/mobile/ios/QuietTests/Info.plist index caa157d240..7f5a67eb0a 100644 --- a/packages/mobile/ios/QuietTests/Info.plist +++ b/packages/mobile/ios/QuietTests/Info.plist @@ -15,10 +15,10 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 2.1.2 + 2.2.0 CFBundleSignature ???? CFBundleVersion - 368 + 369 diff --git a/packages/mobile/package-lock.json b/packages/mobile/package-lock.json index 81d1fcbd42..210e059f3d 100644 --- a/packages/mobile/package-lock.json +++ b/packages/mobile/package-lock.json @@ -1,12 +1,12 @@ { "name": "@quiet/mobile", - "version": "2.1.2", + "version": "2.2.0-alpha.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@quiet/mobile", - "version": "2.1.2", + "version": "2.2.0-alpha.0", "dependencies": { "@peculiar/webcrypto": "^1.4.3", "@react-native-clipboard/clipboard": "^1.13.2", diff --git a/packages/mobile/package.json b/packages/mobile/package.json index 12a222a0ba..b29f0f248d 100644 --- a/packages/mobile/package.json +++ b/packages/mobile/package.json @@ -1,6 +1,6 @@ { "name": "@quiet/mobile", - "version": "2.1.2", + "version": "2.2.0-alpha.0", "scripts": { "build": "tsc -p tsconfig.build.json --noEmit", "storybook-android": "ENVFILE=.env.storybook react-native run-android --mode=storybookDebug --appIdSuffix=storybook.debug", From 7786e262096252c65d49318178bbcb8c6dd9494d Mon Sep 17 00:00:00 2001 From: Lucas Leblow Date: Thu, 4 Apr 2024 13:06:41 -0700 Subject: [PATCH 03/48] Update packages CHANGELOG.md --- packages/desktop/CHANGELOG.md | 43 ++++++++++------------------------- packages/mobile/CHANGELOG.md | 41 ++++++++++----------------------- 2 files changed, 24 insertions(+), 60 deletions(-) diff --git a/packages/desktop/CHANGELOG.md b/packages/desktop/CHANGELOG.md index a3a0c229f7..59c0741d0f 100644 --- a/packages/desktop/CHANGELOG.md +++ b/packages/desktop/CHANGELOG.md @@ -1,40 +1,19 @@ -# Change Log - -All notable changes to this project will be documented in this file. -See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - -# [2.2.0-alpha.0](/compare/@quiet/desktop@2.0.3-alpha.15...@quiet/desktop@2.2.0-alpha.0) (2024-04-04) - - -### Bug Fixes - -* "Invite A Friend" tab now reads "Add Members" (#2234) 049cd56, closes #2234 -* Add `patch` to requirements documentation (#1766) 8eb6fcc, closes #1766 -* Allow JPEG and GIF images as profile photos #2332 (#2353) 233725f, closes #2332 #2353 -* Clean up desktop UI console errors/warnings (#2226) 570a7a9, closes #2226 -* cleanup username creation component (#2216) 1d03995, closes #2216 -* create jdenticon from pubKey, not username - to distinguish user… (#2207) fd8bd06, closes #2207 -* Enable channel context menu for all users (#2206) 02f6809, closes #2206 -* Make community name field text visible on create community page (#2233) 0f4a33f, closes #2233 -* make sure local peer's address in in invitation link (#2268) 53f1ec9, closes #2268 -* pasting multiple files #1987 (#2306) 7c6b669, closes #1987 #2306 -* Quick fix for intro message race condition (#2376) d4f7449, closes #2376 -* Remove duplicate introduction messages once again (#2296) 655a812, closes #2296 -* Updating channel naming logic (#2307) 38b007e, closes #2307 - - -### Features - -* Add user profile feature for desktop (#1923) d016be5, closes #1923 -* Feat/519 big emoji messages (#2389) 71c8b22, closes #2389 - +[unreleased] +* Refactored package.json to have consistent license "GPL-3.0-or-later" + +# Refactorings: +* Use ack for CREATE_NETWORK and simplify +# Fixes -[unreleased] +* Allow JPEG and GIF files as profile photos ([#2332](https://github.com/TryQuiet/quiet/issues/2332)) +* Fixes issues with recreating general channel when deleted while offline ([#2334](https://github.com/TryQuiet/quiet/issues/2334)) +# New features +* Add utilities for emoji detection in messages and make all-emoji message larger font size ([#519](https://github.com/TryQuiet/quiet/issues/519)) [2.1.2] @@ -48,6 +27,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline # Fixes: +* Fixes channel name creation logic * Remove duplicate introduction messages once again * Prevent channel creation with names that start with special character, then a hyphen * Choose random ports for Tor services (iOS) @@ -294,3 +274,4 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline * C4 for Quiet architecture. Context and Container diagrams. * Invite tab as default in settings + diff --git a/packages/mobile/CHANGELOG.md b/packages/mobile/CHANGELOG.md index 61c9d5037b..59c0741d0f 100644 --- a/packages/mobile/CHANGELOG.md +++ b/packages/mobile/CHANGELOG.md @@ -1,38 +1,19 @@ -# Change Log - -All notable changes to this project will be documented in this file. -See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - -# [2.2.0-alpha.0](/compare/@quiet/mobile@2.0.3-alpha.17...@quiet/mobile@2.2.0-alpha.0) (2024-04-04) - - -### Bug Fixes - -* Add `patch` to requirements documentation (#1766) 8eb6fcc, closes #1766 -* Add retry ability to tor-control and update Tor port on resume (#2360) 9517f08, closes #2360 -* ask push notification runtime permission on Android (#2213) 2f92e88, closes #2213 -* calling init websocket connection (#2261) fe1d9dd, closes #2261 -* cleanup username creation component (#2216) 1d03995, closes #2216 -* create jdenticon from pubKey, not username - to distinguish user… (#2207) fd8bd06, closes #2207 -* deep linking issues (#2154) 2867264, closes #2154 #1970 -* delay node start (#2300) 810f7c3, closes #2300 -* make sure local peer's address in in invitation link (#2268) 53f1ec9, closes #2268 -* Reduce max random port on iOS (#2402) 2c783aa, closes #2402 -* Updating channel naming logic (#2307) 38b007e, closes #2307 -* Use useLegacyPackaging feature in Gradle (#2384) 125ec4e, closes #2384 - - -### Features - -* Add user profile feature for desktop (#1923) d016be5, closes #1923 - +[unreleased] +* Refactored package.json to have consistent license "GPL-3.0-or-later" + +# Refactorings: +* Use ack for CREATE_NETWORK and simplify +# Fixes -[unreleased] +* Allow JPEG and GIF files as profile photos ([#2332](https://github.com/TryQuiet/quiet/issues/2332)) +* Fixes issues with recreating general channel when deleted while offline ([#2334](https://github.com/TryQuiet/quiet/issues/2334)) +# New features +* Add utilities for emoji detection in messages and make all-emoji message larger font size ([#519](https://github.com/TryQuiet/quiet/issues/519)) [2.1.2] @@ -46,6 +27,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline # Fixes: +* Fixes channel name creation logic * Remove duplicate introduction messages once again * Prevent channel creation with names that start with special character, then a hyphen * Choose random ports for Tor services (iOS) @@ -292,3 +274,4 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline * C4 for Quiet architecture. Context and Container diagrams. * Invite tab as default in settings + From c35ec3c7ff4f1ce09898d5c11e73c33a99cf1bec Mon Sep 17 00:00:00 2001 From: Lucas Leblow Date: Mon, 8 Apr 2024 12:49:50 -0700 Subject: [PATCH 04/48] fix: hanging process functionality killing wrong processes (#2419) When using backend data directories like `Quietdev`, `Quietdev1`, `Quietdev2', restarting the process using `Quietdev` can result in that process killing the other backend processes due to the grep command used for finding hanging processes. Example: ``` user (Quietdev1): desktop:main:main Forked backend, PID: 214543 owner (Quietdev): Found 1 hanging backend process(es) with pid(s) 214543. Killing... ``` --- packages/common/src/process.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/common/src/process.ts b/packages/common/src/process.ts index a5d85b6d8a..fdc6111a4b 100644 --- a/packages/common/src/process.ts +++ b/packages/common/src/process.ts @@ -1,5 +1,8 @@ import { type SupportedPlatform } from '@quiet/types' +/** + * Commands should output hanging backend pid + */ export const hangingBackendProcessCommand = ({ backendBundlePath, dataDir, @@ -7,13 +10,10 @@ export const hangingBackendProcessCommand = ({ backendBundlePath: string dataDir: string }): string => { - /** - * Commands should output hanging backend pid - */ const byPlatform = { - android: `pgrep -af "${backendBundlePath}" | grep -v pgrep | grep "${dataDir}" | awk '{print $1}'`, - linux: `pgrep -af "${backendBundlePath}" | grep -v egrep | grep "${dataDir}" | awk '{print $1}'`, - darwin: `ps -A | grep "${backendBundlePath}" | grep -v egrep | grep "${dataDir}" | awk '{print $1}'`, + android: `pgrep -af "${backendBundlePath}" | grep -v pgrep | grep -e "${dataDir}$" -e "${dataDir}[[:space:]]" | awk '{print $1}'`, + linux: `pgrep -af "${backendBundlePath}" | grep -v egrep | grep -e "${dataDir}$" -e "${dataDir}[[:space:]]" | awk '{print $1}'`, + darwin: `ps -A | grep "${backendBundlePath}" | grep -v egrep | grep -e "${dataDir}$" -e "${dataDir}[[:space:]]" | awk '{print $1}'`, win32: `powershell "Get-WmiObject Win32_process -Filter {commandline LIKE '%${backendBundlePath.replace( /\\/g, '\\\\' From 595d8966ffc7e7a8b83d5acebc3f1cad2726bd10 Mon Sep 17 00:00:00 2001 From: Lucas Leblow Date: Mon, 8 Apr 2024 14:29:41 -0700 Subject: [PATCH 05/48] fix: Adds a quick fix for the iOS sync issue after suspend (#2414) iOS appears to not be syncing correctly after resuming from suspend. It looks like this is due to the libp2p auto-dialler getting stuck connecting to the Tor HTTP tunnel port which is replaced after suspend/resume. This commit enables the WebsocketOverTor transport to use the `dialTimeout` correctly. But still this means the current dialer waits for `dialTimeout` to try another peer, and we currently have `dialTimeout` set to 2 minutes. Ideally, we can find a better approach for closing any sockets when suspending the app or detecting when we are trying to connect to a port that nothing is listening on, if that's possible. --- packages/backend/src/nest/app.module.ts | 6 +++--- packages/backend/src/nest/websocketOverTor/index.ts | 1 + packages/mobile/src/App.tsx | 1 - 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/backend/src/nest/app.module.ts b/packages/backend/src/nest/app.module.ts index 03e987a178..01b4215930 100644 --- a/packages/backend/src/nest/app.module.ts +++ b/packages/backend/src/nest/app.module.ts @@ -105,7 +105,7 @@ export class AppModule { io.engine.use((req, res, next) => { const authHeader = req.headers['authorization'] if (!authHeader) { - console.error('No authorization header') + console.error('Backend server: No authorization header') res.writeHead(401, 'No authorization header') res.end() return @@ -113,7 +113,7 @@ export class AppModule { const token = authHeader && authHeader.split(' ')[1] if (!token) { - console.error('No auth token') + console.error('Backend server: No auth token') res.writeHead(401, 'No authorization token') res.end() return @@ -122,7 +122,7 @@ export class AppModule { if (verifyToken(options.socketIOSecret, token)) { next() } else { - console.error('Wrong basic token') + console.error('Backend server: Unauthorized') res.writeHead(401, 'Unauthorized') res.end() } diff --git a/packages/backend/src/nest/websocketOverTor/index.ts b/packages/backend/src/nest/websocketOverTor/index.ts index 929ac78cb2..e714970e8b 100644 --- a/packages/backend/src/nest/websocketOverTor/index.ts +++ b/packages/backend/src/nest/websocketOverTor/index.ts @@ -81,6 +81,7 @@ export class WebSockets extends EventEmitter { websocket: { ...this._websocketOpts, }, + signal: options.signal, }) } catch (e) { log.error('error connecting to %s. Details: %s', ma, e.message) diff --git a/packages/mobile/src/App.tsx b/packages/mobile/src/App.tsx index b3c64c879b..f5128ddc39 100644 --- a/packages/mobile/src/App.tsx +++ b/packages/mobile/src/App.tsx @@ -85,7 +85,6 @@ function App(): JSX.Element { ref={navigationRef} linking={linking} onReady={() => { - dispatch(initActions.blindWebsocketConnection()) dispatch(navigationActions.redirection()) }} > From c089c688f7984c8f6a6aba1a4c3c237c6942efa7 Mon Sep 17 00:00:00 2001 From: Lucas Leblow Date: Tue, 9 Apr 2024 19:24:20 -0700 Subject: [PATCH 06/48] fix: Remove unused dmPublicKey to prevent UI delay during joining (#2392) --- .../backend/src/nest/common/client-server.ts | 1 - .../registration/registration.service.spec.ts | 7 ------ .../src/nest/storage/storage.service.spec.ts | 4 ---- .../src/nest/storage/storage.service.ts | 5 ++--- .../components/Channel/Channel.stories.cy.tsx | 4 ---- .../components/Channel/Channel.stories.tsx | 4 ---- packages/identity/src/common.ts | 22 +++++-------------- packages/identity/src/createUserCert.ts | 13 +++++------ packages/identity/src/createUserCsr.ts | 16 +++++++------- packages/identity/src/test/helpers.ts | 3 --- packages/identity/src/test/index.test.ts | 3 --- .../UsernameRegistration.component.tsx | 2 +- .../UsernameRegistration.test.tsx | 6 +---- .../appConnection/connection.slice.test.ts | 1 - .../createNetwork/createNetwork.saga.ts | 4 ---- .../checkLocalCsr/checkLocalCsr.saga.test.ts | 1 - .../src/sagas/identity/identity.types.ts | 12 ---------- .../registerUsername.saga.test.ts | 3 --- .../registerUsername/registerUsername.saga.ts | 2 -- .../deleteChannel/deleteChannel.saga.test.ts | 1 - .../publicChannels.selectors.test.ts | 1 - .../src/sagas/users/const/certFieldTypes.ts | 1 - .../userProfile/saveUserProfile.saga.test.ts | 1 - .../userProfile/userProfile.selectors.test.ts | 1 - .../src/sagas/users/users.selectors.test.ts | 6 ----- .../src/sagas/users/users.selectors.ts | 6 ----- .../src/sagas/users/users.types.ts | 1 - .../src/utils/tests/factories.ts | 5 ----- packages/types/src/identity.ts | 12 ---------- packages/types/src/user.ts | 1 - 30 files changed, 23 insertions(+), 126 deletions(-) diff --git a/packages/backend/src/nest/common/client-server.ts b/packages/backend/src/nest/common/client-server.ts index 71e22c4728..b94f8f0b2c 100644 --- a/packages/backend/src/nest/common/client-server.ts +++ b/packages/backend/src/nest/common/client-server.ts @@ -9,7 +9,6 @@ export const createUsersCerts = async ( nickname: 'dev99damian1', commonName: onion, peerId: 'Qmf3ySkYqLET9xtAtDzvAr5Pp3egK1H3C5iJAZm1SpLert', - dmPublicKey: 'dmPublicKey1', signAlg: configCrypto.signAlg, hashAlg: configCrypto.hashAlg, } diff --git a/packages/backend/src/nest/registration/registration.service.spec.ts b/packages/backend/src/nest/registration/registration.service.spec.ts index fbaa2a6191..1ccbcf4dd1 100644 --- a/packages/backend/src/nest/registration/registration.service.spec.ts +++ b/packages/backend/src/nest/registration/registration.service.spec.ts @@ -46,7 +46,6 @@ describe('RegistrationService', () => { nickname: 'userName', commonName: 'nqnw4kc4c77fb47lk52m5l57h4tcxceo7ymxekfn7yh5m66t4jv2olad.onion', peerId: 'Qmf3ySkYqLET9xtAtDzvAr5Pp3egK1H3C5iJAZm1SpLEp6', - dmPublicKey: 'testdmPublicKey', signAlg: configCrypto.signAlg, hashAlg: configCrypto.hashAlg, }) @@ -88,7 +87,6 @@ describe('RegistrationService', () => { nickname: 'alice', commonName: 'nqnw4kc4c77fb47lk52m5l57h4tcxceo7ymxekfn7yh5m66t4jv2olad.onion', peerId: 'Qmf3ySkYqLET9xtAtDzvAr5Pp3egK1H3C5iJAZm1SpLEp6', - dmPublicKey: 'testdmPublicKey', signAlg: configCrypto.signAlg, hashAlg: configCrypto.hashAlg, }) @@ -122,7 +120,6 @@ describe('RegistrationService', () => { nickname: 'userName2', commonName: 'nqnw4kc4c77fb47lk52m5l57h4tcxceo7ymxekfn7yh5m66t4jv2olad.onion', peerId: 'Qmf3ySkYqLET9xtAtDzvAr5Pp3egK1H3C5iJAZm1SpLEp6', - dmPublicKey: 'testdmPublicKey', signAlg: configCrypto.signAlg, hashAlg: configCrypto.hashAlg, }) @@ -136,7 +133,6 @@ describe('RegistrationService', () => { nickname: 'karol', commonName: 'nqnw4kc4c77fb47lk52m5l57h4tcxceo7ymxekfn7yh5m66t4jv2olad.onion', peerId: 'Qmf3ySkYqLET9xtAtDzvAr5Pp3egK1H3C5iJAZm1SpLEp6', - dmPublicKey: 'testdmPublicKey', signAlg: configCrypto.signAlg, hashAlg: configCrypto.hashAlg, }) @@ -144,7 +140,6 @@ describe('RegistrationService', () => { nickname: 'karol', commonName: 'nnnnnnc4c77fb47lk52m5l57h4tcxceo7ymxekfn7yh5m66t4jv2olad.onion', peerId: 'QmffffffqLET9xtAtDzvAr5Pp3egK1H3C5iJAZm1SpLEp6', - dmPublicKey: 'testdmPublicKey', signAlg: configCrypto.signAlg, hashAlg: configCrypto.hashAlg, }) @@ -187,7 +182,6 @@ describe('RegistrationService', () => { nickname: 'alice', commonName: 'nqnw4kc4c77fb47lk52m5l57h4tcxceo7ymxekfn7yh5m66t4jv2olad.onion', peerId: 'Qmf3ySkYqLET9xtAtDzvAr5Pp3egK1H3C5iJAZm1SpLEp6', - dmPublicKey: 'testdmPublicKey', signAlg: configCrypto.signAlg, hashAlg: configCrypto.hashAlg, }) @@ -196,7 +190,6 @@ describe('RegistrationService', () => { nickname: 'alice', commonName: 'nnnnnnc4c77fb47lk52m5l57h4tcxceo7ymxekfn7yh5m66t4jv2olad.onion', peerId: 'QmffffffqLET9xtAtDzvAr5Pp3egK1H3C5iJAZm1SpLEp6', - dmPublicKey: 'testdmPublicKey', signAlg: configCrypto.signAlg, hashAlg: configCrypto.hashAlg, }) diff --git a/packages/backend/src/nest/storage/storage.service.spec.ts b/packages/backend/src/nest/storage/storage.service.spec.ts index 0cfdef6888..fa65bcd37b 100644 --- a/packages/backend/src/nest/storage/storage.service.spec.ts +++ b/packages/backend/src/nest/storage/storage.service.spec.ts @@ -525,15 +525,11 @@ describe('StorageService', () => { { onionAddress: '6vu2bxki777it3cpayv6fq6vpl4ke3kzj7gxicfygm55dhhtphyfdvyd.onion', peerId: 'QmXRY4rhAx8Muq8dMGkr9qknJdE6UHZDdGaDRTQEbwFN5b', - dmPublicKey: - '299abdb3a1c456245fa65560624ba583a5d5e35d5945ee14d548df5d37f5a516b281f71e9c66d3e9145a80fb59ce3baae611dd621a838d1cd98676e7f1901261380133ba9cb1e52ae54a52d69134f032368de3caa9ab69dcfce6e9afcc192ce21129d1131efce0994a7f028c8c9977dd403c9509bd9e149e0081f2071ddce0b7fc217756da7deb9e16d57e3a41300c7087a96df9ee5a82d197cbeabfc4011251e2cf7d481161672d94b82df0ea7e593a4bc81919b1516806f26b32dd9774cd11237d98cd346be7a19715ae15c90998e715f095048f8227403fb8cf915ca035ca1e5ef033d990a4dc84750a7de65fc9cc928e773250cd68c625754400d2e836f3f4bb0654a56aad820c3e0e977b304eaec2e0a193c4aac8dfaaee614671ecd11a40317957b56fa8f099d1f6684c826e4984d0ebb8', username: 'o', }, { onionAddress: 'y7yczmugl2tekami7sbdz5pfaemvx7bahwthrdvcbzw5vex2crsr26qd.onion', peerId: 'QmT18UvnUBkseMc3SqnfPxpHwN8nzLrJeNSLZtc8rAFXhz', - dmPublicKey: - '050bf2be48a25c47ff3cb2a7b11d416bd02162c54ef28e7a6e77ed527a15cc19516605332c9bce4aba0f93f1266d5cfea2484cadc60d802587bf1ac0a87555ab42513fa6e2f7c06d094a2dc7a38f147a792b94022fad21a1dd6e02784aa1cc891bdb6ca0cf5a4be92b9ff93e83844206388f676e5c0bb406fd436beb0c8843c7a5061a3830a9bc88ea7b112852b19395d5feb1ae75d8082ff3c9473ce4612ec9c2f35489a0010bcf0136dd333638cd21a734b1f192f494371691f4b9c459ffa2d637aa70e8973af99dc94e417b766a8462afbc183fe6fc8b4220887de6a98d97a69cba851d0062b609439b6c6728be87cccc9f6680073d5ed8716611e2ca54f49bbc43c6aa49e02bcb1eecc79e5e491e063b95104e8e43ad09fac6d930399ff2b6c3a49c5b6117c8dc03db9300c0ca0364f29425', username: 'o', }, ]) diff --git a/packages/backend/src/nest/storage/storage.service.ts b/packages/backend/src/nest/storage/storage.service.ts index 54d7a65b0a..1d9ba7eafe 100644 --- a/packages/backend/src/nest/storage/storage.service.ts +++ b/packages/backend/src/nest/storage/storage.service.ts @@ -738,9 +738,8 @@ export class StorageService extends EventEmitter { const onionAddress = getReqFieldValue(parsedCert, CertFieldsTypes.commonName) const peerId = getReqFieldValue(parsedCert, CertFieldsTypes.peerId) const username = getReqFieldValue(parsedCert, CertFieldsTypes.nickName) - const dmPublicKey = getReqFieldValue(parsedCert, CertFieldsTypes.dmPublicKey) - if (!onionAddress || !peerId || !username || !dmPublicKey) continue - allUsers.push({ onionAddress, peerId, username, dmPublicKey }) + if (!onionAddress || !peerId || !username) continue + allUsers.push({ onionAddress, peerId, username }) } return allUsers } diff --git a/packages/desktop/src/renderer/components/Channel/Channel.stories.cy.tsx b/packages/desktop/src/renderer/components/Channel/Channel.stories.cy.tsx index 0a302125e0..b9c82b83be 100644 --- a/packages/desktop/src/renderer/components/Channel/Channel.stories.cy.tsx +++ b/packages/desktop/src/renderer/components/Channel/Channel.stories.cy.tsx @@ -66,10 +66,6 @@ const Template: ComponentStory = () => { privKey: 'privKey', pubKey: 'pubKey', }, - dmKeys: { - publicKey: 'publicKey', - privateKey: 'privateKey', - }, userCsr: { userCsr: 'userCsr', userKey: 'userKey', diff --git a/packages/desktop/src/renderer/components/Channel/Channel.stories.tsx b/packages/desktop/src/renderer/components/Channel/Channel.stories.tsx index 36593326c1..948bfc678a 100644 --- a/packages/desktop/src/renderer/components/Channel/Channel.stories.tsx +++ b/packages/desktop/src/renderer/components/Channel/Channel.stories.tsx @@ -25,10 +25,6 @@ const args: Partial = { privKey: 'privKey', pubKey: 'pubKey', }, - dmKeys: { - publicKey: 'publicKey', - privateKey: 'privateKey', - }, userCsr: { userCsr: 'userCsr', userKey: 'userKey', diff --git a/packages/identity/src/common.ts b/packages/identity/src/common.ts index 3b6f828e0b..dbd7619b7b 100644 --- a/packages/identity/src/common.ts +++ b/packages/identity/src/common.ts @@ -9,6 +9,7 @@ export enum CertFieldsTypes { subjectAltName = '2.5.29.17', nickName = '1.3.6.1.4.1.50715.2.1', peerId = '1.3.6.1.2.1.15.3.1.1', + // DEPRECATED dmPublicKey = '1.2.840.113549.1.9.12', } @@ -100,17 +101,9 @@ export const getCertFieldValue = (cert: Certificate, fieldType: CertFieldsTypes } else { const ext = cert.extensions?.find(tav => tav.extnID === fieldType) if (ext) { - if (fieldType === CertFieldsTypes.dmPublicKey) { - const extObj = ext?.extnValue.valueBlock.value[0] - // @ts-ignore - const arrayBuffer = extObj.valueBlock.valueHex - - return arrayBufferToHexString(arrayBuffer) - } else { - const extObj = ext?.extnValue.valueBlock.value[0] - // @ts-ignore - return extObj.valueBlock.value - } + const extObj = ext?.extnValue.valueBlock.value[0] + // @ts-ignore + return extObj.valueBlock.value } else { return null } @@ -131,12 +124,7 @@ export const getReqFieldValue = ( } else { const ext = csr.attributes?.find(tav => tav.type === fieldType) if (ext) { - if (fieldType === CertFieldsTypes.dmPublicKey) { - const extObj = ext.values[0].valueBlock.valueHex - return arrayBufferToHexString(extObj) - } else { - return ext.values[0].valueBlock.value - } + return ext.values[0].valueBlock.value } else { return null } diff --git a/packages/identity/src/createUserCert.ts b/packages/identity/src/createUserCert.ts index d8e53b4837..c8cb455bcf 100644 --- a/packages/identity/src/createUserCert.ts +++ b/packages/identity/src/createUserCert.ts @@ -69,14 +69,17 @@ async function generateUserCertificate({ ], }) const attr: Attribute[] | undefined = pkcs10.attributes - let dmPubKey = null let nickname = null let peerId = null let onionAddress = null let altNames try { - dmPubKey = attr?.[1].values[0].valueBlock.valueHex + // publicKey = attr[0] + + // DEPRECATED + // dmPublicKey = attr[1] + nickname = attr?.[2].values[0].valueBlock.value peerId = attr?.[3].values[0].valueBlock.value onionAddress = attr?.[4].values[0].valueBlock.value @@ -89,6 +92,7 @@ async function generateUserCertificate({ ], }) } catch (err) { + console.error(err) throw new Error('Cannot get certificate request extension') } @@ -114,11 +118,6 @@ async function generateUserCertificate({ extnValue: extKeyUsage.toSchema().toBER(false), parsedValue: extKeyUsage, // Parsed value for well-known extensions }), - new Extension({ - extnID: CertFieldsTypes.dmPublicKey, - critical: false, - extnValue: new OctetString({ valueHex: dmPubKey }).toBER(false), - }), new Extension({ extnID: CertFieldsTypes.nickName, critical: false, diff --git a/packages/identity/src/createUserCsr.ts b/packages/identity/src/createUserCsr.ts index eee73dbc27..1266f1f6c3 100644 --- a/packages/identity/src/createUserCsr.ts +++ b/packages/identity/src/createUserCsr.ts @@ -20,13 +20,11 @@ export const createUserCsr = async ({ nickname, commonName, peerId, - dmPublicKey, existingKeyPair, }: { nickname: string commonName: string peerId: string - dmPublicKey: string signAlg: string hashAlg: string existingKeyPair?: CryptoKeyPair @@ -35,7 +33,6 @@ export const createUserCsr = async ({ nickname, commonName, peerId, - dmPublicKey, signAlg: config.signAlg, hashAlg: config.hashAlg, existingKeyPair, @@ -59,7 +56,6 @@ async function requestCertificate({ nickname, commonName, peerId, - dmPublicKey, signAlg = config.signAlg, hashAlg = config.hashAlg, existingKeyPair, @@ -67,15 +63,12 @@ async function requestCertificate({ nickname: string commonName: string peerId: string - dmPublicKey: string signAlg: string hashAlg: string existingKeyPair?: CryptoKeyPair }): Promise { const keyPair: CryptoKeyPair = existingKeyPair ? existingKeyPair : await generateKeyPair({ signAlg }) - const arrayBufferDmPubKey = hexStringToArrayBuffer(dmPublicKey) - const pkcs10 = new CertificationRequest({ version: 0, attributes: [], @@ -110,10 +103,17 @@ async function requestCertificate({ }).toSchema(), ], }), + + // DEPRECATED + // + // We can only remove this attribute when all owners upgrade to + // the version that contains this commit. Otherwise, there could + // be Quiet instances that still reference this attribute. new Attribute({ type: CertFieldsTypes.dmPublicKey, - values: [new OctetString({ valueHex: arrayBufferDmPubKey })], + values: [new OctetString({ valueHex: hexStringToArrayBuffer('') })], }), + new Attribute({ type: CertFieldsTypes.nickName, values: [new PrintableString({ value: nickname })], diff --git a/packages/identity/src/test/helpers.ts b/packages/identity/src/test/helpers.ts index 68760bfbb8..b663df43d2 100644 --- a/packages/identity/src/test/helpers.ts +++ b/packages/identity/src/test/helpers.ts @@ -9,7 +9,6 @@ export const userData = { nickname: 'userName', commonName: 'nqnw4kc4c77fb47lk52m5l57h4tcxceo7ymxekfn7yh5m66t4jv2olad.onion', peerId: 'Qmf3ySkYqLET9xtAtDzvAr5Pp3egK1H3C5iJAZm1SpLEp6', - dmPublicKey: '0bfb475810c0e26c9fab590d47c3d60ec533bb3c451596acc3cd4f21602e9ad9', signAlg: config.signAlg, hashAlg: config.hashAlg, } @@ -62,7 +61,6 @@ export const createUserCertificateTestHelper = async ( nickname: string commonName: string peerId: string - dmPublicKey: string }, rootCA?: Pick | null ): Promise<{ @@ -73,7 +71,6 @@ export const createUserCertificateTestHelper = async ( nickname: user.nickname, commonName: user.commonName, peerId: user.peerId, - dmPublicKey: user.dmPublicKey, signAlg: config.signAlg, hashAlg: config.hashAlg, }) diff --git a/packages/identity/src/test/index.test.ts b/packages/identity/src/test/index.test.ts index 23a1f69dfb..b6af8010ea 100644 --- a/packages/identity/src/test/index.test.ts +++ b/packages/identity/src/test/index.test.ts @@ -87,7 +87,6 @@ describe('Certificate', () => { [CertFieldsTypes.commonName]: userData.commonName, [CertFieldsTypes.nickName]: userData.nickname, [CertFieldsTypes.peerId]: userData.peerId, - [CertFieldsTypes.dmPublicKey]: userData.dmPublicKey, } type CertFieldsTypesKeys = keyof typeof certTypeData @@ -107,7 +106,6 @@ describe('Certificate', () => { [CertFieldsTypes.commonName]: userData.commonName, [CertFieldsTypes.nickName]: userData.nickname, [CertFieldsTypes.peerId]: userData.peerId, - [CertFieldsTypes.dmPublicKey]: userData.dmPublicKey, } type CertFieldsTypesKeys = keyof typeof certTypeData @@ -126,7 +124,6 @@ describe('Certificate', () => { 'MIIB7TCCAZMCBgF641h5xzAKBggqhkjOPQQDAjASMRAwDgYDVQQDEwdaYmF5IENBMB4XDTIxMDcyNjE1MDQyNFoXDTMwMDIwMTAwMDAwMFowgc4xgcswHAYKKwYBBAGDjBsCARMOZGV2OTlkZXY5OXlvZGEwPwYDVQQDEzgzNWNzNmZramJoZmJiMnppYnIzNm5rdXY0cWlld2x2NXBmcGprbHh2N2xtcGphM2hydTN3NDdpZDA7BgkrBgECAQ8DAQETLlFtVmIxbUZ2Z1hKZXRKS0o1NmRtR1Q2Rkd1TnJtM0VhVFZ6V3VHaGtxcjZodjUwLQYJKoZIhvcNAQkMBCCf3wijnripB3ZADnDgT1ZIr1zUGjHVZI2K4kt6Yb7CazBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABDKw/zcoE2Vahw3q9CdRQsCXikFU8PhEIN/y65hrB6yAxWH4Ut9QBKMMAnaG8JlzvEeaScQiu5Jyyx0O0xAadQ+jHTAbMAwGA1UdEwQFMAMCAQMwCwYDVR0PBAQDAgAGMAoGCCqGSM49BAMCA0gAMEUCIQCRz+6W3K3SI7Q7uYDVVIJXnWud/DGvpqHCuLJ+gnJLMgIgBmS1D8s1xnGOQpARx40vus4b/f49LQeG2YxPCSHVQOM=' ) - expect(getCertFieldValue(parsedCert, CertFieldsTypes.dmPublicKey)).toEqual(null) expect(getCertFieldValue(parsedCert, CertFieldsTypes.subjectAltName)).toEqual(null) expect(getCertFieldValue(parsedCert, CertFieldsTypes.peerId)).toEqual(null) expect(getCertFieldValue(parsedCert, CertFieldsTypes.nickName)).toEqual(null) diff --git a/packages/mobile/src/components/Registration/UsernameRegistration.component.tsx b/packages/mobile/src/components/Registration/UsernameRegistration.component.tsx index 236eb5fe3c..93e10f684e 100644 --- a/packages/mobile/src/components/Registration/UsernameRegistration.component.tsx +++ b/packages/mobile/src/components/Registration/UsernameRegistration.component.tsx @@ -124,7 +124,7 @@ export const UsernameRegistration: FC = ({ hint={ isNewUser ? 'Your username cannot have any spaces or special characters, must be lowercase letters and numbers only.' - : 'Your username will be public, but you can choose any name you like. No spaces or special characters. Lowercase letters and numbers only. ' + : 'Your username will be public, but you can choose any name you like. No spaces or special characters. Lowercase letters and numbers only.' } disabled={loading} validation={inputError} diff --git a/packages/mobile/src/components/Registration/UsernameRegistration.test.tsx b/packages/mobile/src/components/Registration/UsernameRegistration.test.tsx index 057b535a23..b9b0fc51fa 100644 --- a/packages/mobile/src/components/Registration/UsernameRegistration.test.tsx +++ b/packages/mobile/src/components/Registration/UsernameRegistration.test.tsx @@ -254,15 +254,11 @@ describe('UsernameRegistration', () => { currentUsername={'john'} registeredUsers={{ 'BCidRGCBqBPNGNrZ1oml99/qtHjZ6ZtliVzJPpReZk9YC6+aQ1zeooOlpyzv7rNG6nMX2R5ffaVkZZFgEMdNEBg=': { - dmPublicKey: - '1c63a0152f0b0221f96c80aab6777e2569d27a14e991dccd086e34cda1c55d9d6e898efdb9cb5a16d2f90b4155e34abf3261c84e76936ba0105929922feda1fb0615f3254cc56c056cb6144076d0cbdba67cf0fb6687a97d9bb6621bb6b38dcf08aa509f1164212118111f045edc5dab8d315d6e1241cdd10c40883ea420d10d560e48329e086154645035af0668e372a381fbd8aa0912f3581de34b50361cc31adf7a8e811504b6970c9093c058f0fb41ae27df64b09bcb13df84bd23d47a0024463cbb92ee917af3b77b168deb93f6da2d0d13a361969447e16bf249edd872b4797125fa86aad1ce35b1d6ee449359f31c9224a70997d0f0ca38c1f796cede660dd0bb3b1fc9ec251f896bc0aec1603ee8e1278c76d9d1e52adcfa0a06658d631486016efb3b5f44e0c3fb1ce4299834cdf05e', onionAddress: 'zpu47hphczcuyt3auu5pr2knvimf3adm76gt3g7zbspungjbm3tsy3ad.onion', peerId: 'QmPrgB2jSFvr7yP3vbLKMLW4JS9hoA9kj2fhN8VjE2NWvJ', username: 'owner', }, 'BJ50trLih9tvIznBAi69dLNpBV5YiQHCU610UfH3Qm9t3cki9QHWAAhvrfOX+763BH1fwqkOU3JsoBAEF1+USvM=': { - dmPublicKey: - '01161f217d3b372ddefe390b6aed6ada3f21c7ca0d2aacd125840d0c420030b5caec93fbcc1825bb19f38448c1573be814c827b3bce99f6d37b427044687b63799ce0cd7133a654eb2c5644405380399ed0b9140cf0f6d2937008929be8c8ad97cba64e08f7608bb54e0148c8bb30fd2c6df2b8d237cdbfe7ae3d300d896c0ce9e3c5006b687f2e573bedb68152db1954869c21243ef1557eb6d442138ad98e47285f5dd9475fac3a06d2972ae775bdc50d5ab59676d27367c15954b64b0b1a29083ac78934f3f1d0088b73389e29d84f61c60c321770550ecdffa9a24cf0c7fdd279974b5703ce105f22d683530c2f62b36945b3d97f88256f31e1146c3861993a989f5cd99bda647d8744068c3bb44eea407f33109c9c1a0f790f745a6ff2bfd45f5be741fc351549b11d54a4980c4d6d7afb4', onionAddress: 'lr5d3d64p4hx4mw3uue3ufews23jhl6bfqnsimt7j52igjqdv2zrmsyd.onion', peerId: 'QmWwMev68izPUKB1PGxkG3UHHXiappQQAnkvUwPSTzrUyy', username: 'johnny', @@ -577,7 +573,7 @@ describe('UsernameRegistration', () => { } verticalTextAlign="center" > - Your username will be public, but you can choose any name you like. No spaces or special characters. Lowercase letters and numbers only. + Your username will be public, but you can choose any name you like. No spaces or special characters. Lowercase letters and numbers only. { username: alice.nickname, onionAddress: alice.hiddenService.onionAddress, peerId: alice.peerId.id, - dmPublicKey: alice.dmKeys.publicKey, isDuplicated: false, isRegistered: true, pubKey, diff --git a/packages/state-manager/src/sagas/communities/createNetwork/createNetwork.saga.ts b/packages/state-manager/src/sagas/communities/createNetwork/createNetwork.saga.ts index ed84a06819..33003d6df5 100644 --- a/packages/state-manager/src/sagas/communities/createNetwork/createNetwork.saga.ts +++ b/packages/state-manager/src/sagas/communities/createNetwork/createNetwork.saga.ts @@ -6,7 +6,6 @@ import { communitiesActions } from '../communities.slice' import { identityActions } from '../../identity/identity.slice' import { createRootCA } from '@quiet/identity' import { type Community, CommunityOwnership, type Identity, SocketActionTypes } from '@quiet/types' -import { generateDmKeyPair } from '../../../utils/cryptography/cryptography' import { Socket, applyEmitParams } from '../../../types' export function* createNetworkSaga( @@ -55,15 +54,12 @@ export function* createNetworkSaga( yield* put(communitiesActions.setInvitationCodes(invitationPeers)) } - const dmKeys = yield* call(generateDmKeyPair) - // Identities are tied to communities for now const identity: Identity = { id: community.id, nickname: '', hiddenService: network.hiddenService, peerId: network.peerId, - dmKeys, userCsr: null, userCertificate: null, joinTimestamp: null, diff --git a/packages/state-manager/src/sagas/identity/checkLocalCsr/checkLocalCsr.saga.test.ts b/packages/state-manager/src/sagas/identity/checkLocalCsr/checkLocalCsr.saga.test.ts index 160e6ec004..7804f2fc5c 100644 --- a/packages/state-manager/src/sagas/identity/checkLocalCsr/checkLocalCsr.saga.test.ts +++ b/packages/state-manager/src/sagas/identity/checkLocalCsr/checkLocalCsr.saga.test.ts @@ -61,7 +61,6 @@ describe('checkLocalCsr', () => { nickname: 'alice', commonName: identity.hiddenService.onionAddress, peerId: identity.peerId.id, - dmPublicKey: identity.dmKeys.publicKey, signAlg: 'ECDSA', hashAlg: 'sha-256', existingKeyPair, diff --git a/packages/state-manager/src/sagas/identity/identity.types.ts b/packages/state-manager/src/sagas/identity/identity.types.ts index 31bd088788..31420aa396 100644 --- a/packages/state-manager/src/sagas/identity/identity.types.ts +++ b/packages/state-manager/src/sagas/identity/identity.types.ts @@ -10,11 +10,6 @@ export interface UserCsr { pkcs10: CertData } -export interface CreateDmKeyPairPayload { - dmPublicKey: string - dmPrivateKey: string -} - export interface HiddenService { onionAddress: string privateKey: string @@ -26,16 +21,10 @@ export interface PeerId { privKey?: string } -export interface DmKeys { - publicKey: string - privateKey: string -} - export interface Identity { id: string nickname: string hiddenService: HiddenService - dmKeys: DmKeys peerId: PeerId userCsr: UserCsr | null userCertificate: string | null @@ -46,7 +35,6 @@ export interface CreateUserCsrPayload { nickname: string commonName: string peerId: string - dmPublicKey: string signAlg: string hashAlg: string } diff --git a/packages/state-manager/src/sagas/identity/registerUsername/registerUsername.saga.test.ts b/packages/state-manager/src/sagas/identity/registerUsername/registerUsername.saga.test.ts index 112d724cd1..5acf2b3e7b 100644 --- a/packages/state-manager/src/sagas/identity/registerUsername/registerUsername.saga.test.ts +++ b/packages/state-manager/src/sagas/identity/registerUsername/registerUsername.saga.test.ts @@ -48,7 +48,6 @@ describe('registerUsernameSaga', () => { nickname: 'nickname', commonName: identity.hiddenService.onionAddress, peerId: identity.peerId.id, - dmPublicKey: identity.dmKeys.publicKey, signAlg: config.signAlg, hashAlg: config.hashAlg, } @@ -105,7 +104,6 @@ describe('registerUsernameSaga', () => { nickname: newNickname, commonName: identity.hiddenService.onionAddress, peerId: identity.peerId.id, - dmPublicKey: identity.dmKeys.publicKey, signAlg: config.signAlg, hashAlg: config.hashAlg, existingKeyPair: { @@ -247,7 +245,6 @@ describe('registerUsernameSaga', () => { nickname: 'nickname', commonName: identity.hiddenService.onionAddress, peerId: identity.peerId.id, - dmPublicKey: identity.dmKeys.publicKey, signAlg: config.signAlg, hashAlg: config.hashAlg, } diff --git a/packages/state-manager/src/sagas/identity/registerUsername/registerUsername.saga.ts b/packages/state-manager/src/sagas/identity/registerUsername/registerUsername.saga.ts index 6899f03c21..4622539f7f 100644 --- a/packages/state-manager/src/sagas/identity/registerUsername/registerUsername.saga.ts +++ b/packages/state-manager/src/sagas/identity/registerUsername/registerUsername.saga.ts @@ -54,7 +54,6 @@ export function* registerUsernameSaga( nickname, commonName: identity.hiddenService.onionAddress, peerId: identity.peerId.id, - dmPublicKey: identity.dmKeys.publicKey, signAlg: config.signAlg, hashAlg: config.hashAlg, existingKeyPair, @@ -71,7 +70,6 @@ export function* registerUsernameSaga( nickname, commonName: identity.hiddenService.onionAddress, peerId: identity.peerId.id, - dmPublicKey: identity.dmKeys.publicKey, signAlg: config.signAlg, hashAlg: config.hashAlg, } diff --git a/packages/state-manager/src/sagas/publicChannels/deleteChannel/deleteChannel.saga.test.ts b/packages/state-manager/src/sagas/publicChannels/deleteChannel/deleteChannel.saga.test.ts index ac250d9ec4..4b9e0f15fd 100644 --- a/packages/state-manager/src/sagas/publicChannels/deleteChannel/deleteChannel.saga.test.ts +++ b/packages/state-manager/src/sagas/publicChannels/deleteChannel/deleteChannel.saga.test.ts @@ -38,7 +38,6 @@ describe('deleteChannelSaga', () => { username: string | null onionAddress: string | null peerId: string | null - dmPublicKey: string | null pubKey: string } | null diff --git a/packages/state-manager/src/sagas/publicChannels/publicChannels.selectors.test.ts b/packages/state-manager/src/sagas/publicChannels/publicChannels.selectors.test.ts index f392b997f3..b7e92e8272 100644 --- a/packages/state-manager/src/sagas/publicChannels/publicChannels.selectors.test.ts +++ b/packages/state-manager/src/sagas/publicChannels/publicChannels.selectors.test.ts @@ -260,7 +260,6 @@ describe('publicChannelsSelectors', () => { const displayable: Record = {} for (const message of Object.values(msgs)) { const user: User = { - dmPublicKey: '', isDuplicated: false, isRegistered: true, onionAddress: '', diff --git a/packages/state-manager/src/sagas/users/const/certFieldTypes.ts b/packages/state-manager/src/sagas/users/const/certFieldTypes.ts index 161e0dc7f8..da39a0c858 100644 --- a/packages/state-manager/src/sagas/users/const/certFieldTypes.ts +++ b/packages/state-manager/src/sagas/users/const/certFieldTypes.ts @@ -3,7 +3,6 @@ export enum CertFieldsTypes { subjectAltName = '2.5.29.17', nickName = '1.3.6.1.4.1.50715.2.1', peerId = '1.3.6.1.2.1.15.3.1.1', - dmPublicKey = '1.2.840.113549.1.9.12', } export const config = { diff --git a/packages/state-manager/src/sagas/users/userProfile/saveUserProfile.saga.test.ts b/packages/state-manager/src/sagas/users/userProfile/saveUserProfile.saga.test.ts index 3317097d7b..fc363a5753 100644 --- a/packages/state-manager/src/sagas/users/userProfile/saveUserProfile.saga.test.ts +++ b/packages/state-manager/src/sagas/users/userProfile/saveUserProfile.saga.test.ts @@ -28,7 +28,6 @@ describe('saveUserProfileSaga', () => { nickname: '', commonName: '', peerId: '', - dmPublicKey: '', signAlg: '', hashAlg: '', }) diff --git a/packages/state-manager/src/sagas/users/userProfile/userProfile.selectors.test.ts b/packages/state-manager/src/sagas/users/userProfile/userProfile.selectors.test.ts index 02b7a66576..4159216e1d 100644 --- a/packages/state-manager/src/sagas/users/userProfile/userProfile.selectors.test.ts +++ b/packages/state-manager/src/sagas/users/userProfile/userProfile.selectors.test.ts @@ -23,7 +23,6 @@ describe('user profile selectors', () => { nickname: '', commonName: '', peerId: '', - dmPublicKey: '', signAlg: '', hashAlg: '', }) diff --git a/packages/state-manager/src/sagas/users/users.selectors.test.ts b/packages/state-manager/src/sagas/users/users.selectors.test.ts index 50af782c63..edc7fb8671 100644 --- a/packages/state-manager/src/sagas/users/users.selectors.test.ts +++ b/packages/state-manager/src/sagas/users/users.selectors.test.ts @@ -23,7 +23,6 @@ describe('users selectors', () => { let alicePublicKey: string const aliceCertificateData = { - dmPublicKey: '0bfb475810c0e26c9fab590d47c3d60ec533bb3c451596acc3cd4f21602e9ad9', onionAddress: 'nqnw4kc4c77fb47lk52m5l57h4tcxceo7ymxekfn7yh5m66t4jv2olad.onion', peerId: 'Qmf3ySkYqLET9xtAtDzvAr5Pp3egK1H3C5iJAZm1SpLEp6', username: 'alice', @@ -49,10 +48,6 @@ describe('users selectors', () => { peerId: { id: aliceCertificateData.peerId, }, - dmKeys: { - publicKey: aliceCertificateData.dmPublicKey, - privateKey: '', - }, }) const parsedAliceCertificate = parseCertificate(alice.userCertificate!) @@ -82,7 +77,6 @@ describe('users selectors', () => { expect(usersData[alicePublicKey]).toMatchInlineSnapshot(` Object { - "dmPublicKey": "0bfb475810c0e26c9fab590d47c3d60ec533bb3c451596acc3cd4f21602e9ad9", "onionAddress": "nqnw4kc4c77fb47lk52m5l57h4tcxceo7ymxekfn7yh5m66t4jv2olad.onion", "peerId": "Qmf3ySkYqLET9xtAtDzvAr5Pp3egK1H3C5iJAZm1SpLEp6", "username": "alice", diff --git a/packages/state-manager/src/sagas/users/users.selectors.ts b/packages/state-manager/src/sagas/users/users.selectors.ts index d9e2abc656..2f21e34b08 100644 --- a/packages/state-manager/src/sagas/users/users.selectors.ts +++ b/packages/state-manager/src/sagas/users/users.selectors.ts @@ -29,7 +29,6 @@ export const certificatesMapping = createSelector(certificates, certs => { const username = getCertFieldValue(certificate, CertFieldsTypes.nickName) const onionAddress = getCertFieldValue(certificate, CertFieldsTypes.commonName) const peerId = getCertFieldValue(certificate, CertFieldsTypes.peerId) - const dmPublicKey = getCertFieldValue(certificate, CertFieldsTypes.dmPublicKey) || '' if (!username || !onionAddress || !peerId) { console.error(`Could not parse certificate for pubkey ${pubKey}`) @@ -40,7 +39,6 @@ export const certificatesMapping = createSelector(certificates, certs => { username, onionAddress, peerId, - dmPublicKey, }) }) return mapping @@ -58,7 +56,6 @@ export const csrsMapping = createSelector(csrs, csrs => { const username = getReqFieldValue(csr, CertFieldsTypes.nickName) const onionAddress = getReqFieldValue(csr, CertFieldsTypes.commonName) const peerId = getReqFieldValue(csr, CertFieldsTypes.peerId) - const dmPublicKey = getReqFieldValue(csr, CertFieldsTypes.dmPublicKey) || '' if (!username || !onionAddress || !peerId) { console.error(`Could not parse certificate for pubkey ${pubKey}`) @@ -69,7 +66,6 @@ export const csrsMapping = createSelector(csrs, csrs => { username, onionAddress, peerId, - dmPublicKey, }) }) @@ -130,14 +126,12 @@ export const ownerData = createSelector(ownerCertificate, ownerCertificate => { const username = getCertFieldValue(ownerCert, CertFieldsTypes.nickName) const onionAddress = getCertFieldValue(ownerCert, CertFieldsTypes.commonName) const peerId = getCertFieldValue(ownerCert, CertFieldsTypes.peerId) - const dmPublicKey = getCertFieldValue(ownerCert, CertFieldsTypes.dmPublicKey) const pubKey = keyFromCertificate(ownerCert) return { username, onionAddress, peerId, - dmPublicKey, pubKey, } }) diff --git a/packages/state-manager/src/sagas/users/users.types.ts b/packages/state-manager/src/sagas/users/users.types.ts index 689b52cb39..b934c98478 100644 --- a/packages/state-manager/src/sagas/users/users.types.ts +++ b/packages/state-manager/src/sagas/users/users.types.ts @@ -2,7 +2,6 @@ export interface User { username: string onionAddress: string peerId: string - dmPublicKey: string } export interface SendCertificatesResponse { diff --git a/packages/state-manager/src/utils/tests/factories.ts b/packages/state-manager/src/utils/tests/factories.ts index e73b7f84a1..9a0ee68f6a 100644 --- a/packages/state-manager/src/utils/tests/factories.ts +++ b/packages/state-manager/src/utils/tests/factories.ts @@ -93,10 +93,6 @@ export const getFactory = async (store: Store) => { 'ED25519-V3:WND1FoFZyY+c1f0uD6FBWgKvSYl4CdKSizSR7djRekW/rqw5fTw+gN80sGk0gl01sL5i25noliw85zF1BUBRDQ==', }, peerId: createPeerIdTestHelper(), - dmKeys: { - publicKey: '9f016defcbe48829db163e86b28efb10318faf3b109173105e3dc024e951bb1b', - privateKey: '4dcebbf395c0e9415bc47e52c96fcfaf4bd2485a516f45118c2477036b45fc0b', - }, nickname: factory.sequence('Identity.nickname', (n: number) => `user_${n}`), userCsr: undefined, userCertificate: undefined, @@ -115,7 +111,6 @@ export const getFactory = async (store: Store) => { nickname: action.payload.nickname, commonName: action.payload.hiddenService.onionAddress, peerId: action.payload.peerId.id, - dmPublicKey: action.payload.dmKeys.publicKey, }, community.CA ) diff --git a/packages/types/src/identity.ts b/packages/types/src/identity.ts index d00e906774..c2606e2c5d 100644 --- a/packages/types/src/identity.ts +++ b/packages/types/src/identity.ts @@ -10,11 +10,6 @@ export interface UserCsr { pkcs10: CertData } -export interface CreateDmKeyPairPayload { - dmPublicKey: string - dmPrivateKey: string -} - export interface HiddenService { onionAddress: string privateKey: string @@ -26,16 +21,10 @@ export interface PeerId { privKey?: string } -export interface DmKeys { - publicKey: string - privateKey: string -} - export interface Identity { id: string nickname: string hiddenService: HiddenService - dmKeys: DmKeys peerId: PeerId userCsr: UserCsr | null userCertificate: string | null @@ -49,7 +38,6 @@ export interface CreateUserCsrPayload { nickname: string commonName: string peerId: string - dmPublicKey: string signAlg: string hashAlg: string existingKeyPair?: CryptoKeyPair diff --git a/packages/types/src/user.ts b/packages/types/src/user.ts index 49aa963171..f7a3135624 100644 --- a/packages/types/src/user.ts +++ b/packages/types/src/user.ts @@ -2,7 +2,6 @@ export interface UserData { username: string onionAddress: string peerId: string - dmPublicKey: string } export interface User extends UserData { From 7855f009e0541f5086d5b1b9ae84f9eceb285ef5 Mon Sep 17 00:00:00 2001 From: Lucas Leblow Date: Tue, 9 Apr 2024 20:06:44 -0700 Subject: [PATCH 07/48] refactor: Remove SET_COMMUNITY_METADATA event (#2408) --- CHANGELOG.md | 25 ++++++--- .../connections-manager.service.ts | 56 +++++++++---------- .../src/nest/socket/socket.service.spec.ts | 1 - .../backend/src/nest/socket/socket.service.ts | 11 ---- .../src/rtl-tests/community.create.test.tsx | 12 ++-- .../src/rtl-tests/community.join.test.tsx | 1 - .../communities/communities.master.saga.ts | 4 -- .../sagas/communities/communities.slice.ts | 2 - .../createCommunity/createCommunity.saga.ts | 3 - .../sendCommunityCaData.saga.ts | 30 ---------- .../updateCommunityMetadata.saga.ts | 43 -------------- .../src/sagas/identity/identity.types.ts | 23 -------- .../src/sagas/network/network.slice.ts | 1 - .../startConnection/startConnection.saga.ts | 6 -- packages/state-manager/src/types.ts | 5 -- packages/types/src/socket.ts | 2 - 16 files changed, 46 insertions(+), 179 deletions(-) delete mode 100644 packages/state-manager/src/sagas/communities/sendCommunityCaData/sendCommunityCaData.saga.ts delete mode 100644 packages/state-manager/src/sagas/communities/updateCommunityMetadata/updateCommunityMetadata.saga.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 59c0741d0f..e5ad80e5c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,20 +1,28 @@ [unreleased] -* Refactored package.json to have consistent license "GPL-3.0-or-later" - -# Refactorings: +# New features: -* Use ack for CREATE_NETWORK and simplify +# Refactorings: -# Fixes +# Fixes: -* Allow JPEG and GIF files as profile photos ([#2332](https://github.com/TryQuiet/quiet/issues/2332)) -* Fixes issues with recreating general channel when deleted while offline ([#2334](https://github.com/TryQuiet/quiet/issues/2334)) +[2.2.0] -# New features +# New features: * Add utilities for emoji detection in messages and make all-emoji message larger font size ([#519](https://github.com/TryQuiet/quiet/issues/519)) +# Refactorings: + +* Use ack for CREATE_NETWORK and simplify +* Move Community model to the backend + +# Fixes: + +* Allow JPEG and GIF files as profile photos ([#2332](https://github.com/TryQuiet/quiet/issues/2332)) +* Fix issues with recreating general channel when deleted while offline ([#2334](https://github.com/TryQuiet/quiet/issues/2334)) +* Fix package.json license inconsistency + [2.1.2] # Refactorings: @@ -274,4 +282,3 @@ * C4 for Quiet architecture. Context and Container diagrams. * Invite tab as default in settings - diff --git a/packages/backend/src/nest/connections-manager/connections-manager.service.ts b/packages/backend/src/nest/connections-manager/connections-manager.service.ts index b3b29e226e..07353b6fa0 100644 --- a/packages/backend/src/nest/connections-manager/connections-manager.service.ts +++ b/packages/backend/src/nest/connections-manager/connections-manager.service.ts @@ -349,7 +349,7 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI const localAddress = createLibp2pAddress(payload.hiddenService.onionAddress, payload.peerId.id) - const community = { + let community: Community = { id: payload.id, name: payload.name, CA: payload.CA, @@ -371,6 +371,22 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI await this.localDbService.setNetworkInfo(network) await this.launchCommunity({ community, network }) + + const meta = await this.storageService.updateCommunityMetadata({ + id: community.id, + rootCa: community.rootCa as string, + ownerCertificate: community.ownerCertificate as string, + }) + const currentCommunity = await this.localDbService.getCurrentCommunity() + + if (meta && currentCommunity) { + community = { + ...currentCommunity, + ownerOrbitDbIdentity: meta.ownerOrbitDbIdentity, + } + await this.localDbService.setCommunity(community) + } + this.logger(`Created and launched community ${community.id}`) return community @@ -535,7 +551,15 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI // service for now. Both object construction and object // initialization need to happen in order based on dependencies. await this.registrationService.init(this.storageService) - this.logger('storage initialized') + + if (community.CA) { + this.registrationService.setPermsData({ + certificate: community.CA.rootCertString, + privKey: community.CA.rootKeyString, + }) + } + + this.logger('Storage initialized') this.serverIoProvider.io.emit( SocketActionTypes.CONNECTION_PROCESS_INFO, @@ -559,7 +583,6 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI this.socketService.on(SocketActionTypes.CONNECTION, async () => { // Update Frontend with Initialized Communities if (this.communityId) { - console.log('Hunting for heisenbug: Backend initialized community and sent event to state manager') this.serverIoProvider.io.emit(SocketActionTypes.COMMUNITY_LAUNCHED, { id: this.communityId }) console.log('this.libp2pService.connectedPeers', this.libp2pService.connectedPeers) console.log('this.libp2pservice', this.libp2pService) @@ -595,26 +618,6 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI callback(await this.joinCommunity(args)) } ) - // TODO: With the Community model on the backend, there is no need to call - // SET_COMMUNITY_METADATA anymore. We can call updateCommunityMetadata when - // creating the community. - this.socketService.on( - SocketActionTypes.SET_COMMUNITY_METADATA, - async (payload: CommunityMetadata, callback: (response: CommunityMetadata | undefined) => void) => { - const meta = await this.storageService.updateCommunityMetadata(payload) - const community = await this.localDbService.getCurrentCommunity() - - if (meta && community) { - const updatedCommunity = { - ...community, - ownerOrbitDbIdentity: meta.ownerOrbitDbIdentity, - } - await this.localDbService.setCommunity(updatedCommunity) - this.serverIoProvider.io.emit(SocketActionTypes.COMMUNITY_UPDATED, updatedCommunity) - } - callback(meta) - } - ) this.socketService.on(SocketActionTypes.LEAVE_COMMUNITY, async () => { await this.leaveCommunity() }) @@ -624,13 +627,6 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI this.logger(`socketService - ${SocketActionTypes.ADD_CSR}`) await this.storageService?.saveCSR(payload) }) - // TODO: With the Community model on the backend, there is no need to call - // SET_COMMUNITY_CA_DATA anymore. We can call setPermsData when - // creating the community. - this.socketService.on(SocketActionTypes.SET_COMMUNITY_CA_DATA, async (payload: PermsData) => { - this.logger(`socketService - ${SocketActionTypes.SET_COMMUNITY_CA_DATA}`) - this.registrationService.setPermsData(payload) - }) // Public Channels this.socketService.on( diff --git a/packages/backend/src/nest/socket/socket.service.spec.ts b/packages/backend/src/nest/socket/socket.service.spec.ts index 56d30c1275..58fe9f5d82 100644 --- a/packages/backend/src/nest/socket/socket.service.spec.ts +++ b/packages/backend/src/nest/socket/socket.service.spec.ts @@ -61,7 +61,6 @@ describe('SocketService', () => { SocketActionTypes.LAUNCH_COMMUNITY.valueOf(), SocketActionTypes.REGISTER_USER_CERTIFICATE.valueOf(), SocketActionTypes.ADD_CSR.valueOf(), - SocketActionTypes.SET_COMMUNITY_METADATA.valueOf(), ] fragile.forEach(event => { diff --git a/packages/backend/src/nest/socket/socket.service.ts b/packages/backend/src/nest/socket/socket.service.ts index 32d99659fd..d126eb3be1 100644 --- a/packages/backend/src/nest/socket/socket.service.ts +++ b/packages/backend/src/nest/socket/socket.service.ts @@ -179,17 +179,6 @@ export class SocketService extends EventEmitter implements OnModuleInit { this.emit(SocketActionTypes.LIBP2P_PSK_STORED, payload) }) - socket.on( - SocketActionTypes.SET_COMMUNITY_METADATA, - (payload: CommunityMetadata, callback: (response: CommunityMetadata | undefined) => void) => { - this.emit(SocketActionTypes.SET_COMMUNITY_METADATA, payload, callback) - } - ) - - socket.on(SocketActionTypes.SET_COMMUNITY_CA_DATA, (payload: PermsData) => { - this.emit(SocketActionTypes.SET_COMMUNITY_CA_DATA, payload) - }) - // ====== Users ====== socket.on(SocketActionTypes.SET_USER_PROFILE, (profile: UserProfile) => { diff --git a/packages/desktop/src/rtl-tests/community.create.test.tsx b/packages/desktop/src/rtl-tests/community.create.test.tsx index 44653b7730..446409f0e2 100644 --- a/packages/desktop/src/rtl-tests/community.create.test.tsx +++ b/packages/desktop/src/rtl-tests/community.create.test.tsx @@ -20,13 +20,11 @@ import { type NetworkInfo, SavedOwnerCertificatePayload, SocketActionTypes, + type ChannelsReplicatedPayload, + type RegisterOwnerCertificatePayload, + type ResponseLaunchCommunityPayload, } from '@quiet/types' -import { - ChannelsReplicatedPayload, - publicChannels, - RegisterOwnerCertificatePayload, - ResponseLaunchCommunityPayload, -} from '@quiet/state-manager' +import { publicChannels } from '@quiet/state-manager' import Channel from '../renderer/components/Channel/Channel' import LoadingPanel from '../renderer/components/LoadingPanel/LoadingPanel' import { AnyAction } from 'redux' @@ -167,7 +165,6 @@ describe('User', () => { "Modals/openModal", "Identity/registerCertificate", "Communities/createCommunity", - "Communities/sendCommunityCaData", "Files/checkForMissingFiles", "Network/addInitializedCommunity", "Communities/clearInvitationCodes", @@ -176,7 +173,6 @@ describe('User', () => { "PublicChannels/addChannel", "Identity/storeUserCertificate", "Messages/addPublicChannelsMessagesBase", - "Communities/sendCommunityMetadata", "PublicChannels/createGeneralChannel", "PublicChannels/createChannel", "Identity/saveUserCsr", diff --git a/packages/desktop/src/rtl-tests/community.join.test.tsx b/packages/desktop/src/rtl-tests/community.join.test.tsx index 26a5c6ea57..0d6f4c1af9 100644 --- a/packages/desktop/src/rtl-tests/community.join.test.tsx +++ b/packages/desktop/src/rtl-tests/community.join.test.tsx @@ -181,7 +181,6 @@ describe('User', () => { "Modals/openModal", "Identity/registerCertificate", "Communities/launchCommunity", - "Communities/sendCommunityCaData", "Files/checkForMissingFiles", "Network/addInitializedCommunity", "Communities/clearInvitationCodes", diff --git a/packages/state-manager/src/sagas/communities/communities.master.saga.ts b/packages/state-manager/src/sagas/communities/communities.master.saga.ts index 599e9eeee1..c5e1e1a275 100644 --- a/packages/state-manager/src/sagas/communities/communities.master.saga.ts +++ b/packages/state-manager/src/sagas/communities/communities.master.saga.ts @@ -5,8 +5,6 @@ import { connectionActions } from '../appConnection/connection.slice' import { createCommunitySaga } from './createCommunity/createCommunity.saga' import { initCommunities, launchCommunitySaga } from './launchCommunity/launchCommunity.saga' import { createNetworkSaga } from './createNetwork/createNetwork.saga' -import { sendCommunityMetadataSaga } from './updateCommunityMetadata/updateCommunityMetadata.saga' -import { sendCommunityCaDataSaga } from './sendCommunityCaData/sendCommunityCaData.saga' export function* communitiesMasterSaga(socket: Socket): Generator { yield all([ @@ -14,7 +12,5 @@ export function* communitiesMasterSaga(socket: Socket): Generator { takeEvery(connectionActions.torBootstrapped.type, initCommunities), takeEvery(communitiesActions.createCommunity.type, createCommunitySaga, socket), takeEvery(communitiesActions.launchCommunity.type, launchCommunitySaga, socket), - takeEvery(communitiesActions.sendCommunityMetadata.type, sendCommunityMetadataSaga, socket), - takeEvery(communitiesActions.sendCommunityCaData.type, sendCommunityCaDataSaga, socket), ]) } diff --git a/packages/state-manager/src/sagas/communities/communities.slice.ts b/packages/state-manager/src/sagas/communities/communities.slice.ts index c8b49e072b..dae53ca209 100644 --- a/packages/state-manager/src/sagas/communities/communities.slice.ts +++ b/packages/state-manager/src/sagas/communities/communities.slice.ts @@ -35,8 +35,6 @@ export const communitiesSlice = createSlice({ }, }) }, - sendCommunityCaData: state => state, - sendCommunityMetadata: state => state, createNetwork: (state, _action: PayloadAction) => state, resetApp: (state, _action) => state, createCommunity: (state, _action: PayloadAction) => state, diff --git a/packages/state-manager/src/sagas/communities/createCommunity/createCommunity.saga.ts b/packages/state-manager/src/sagas/communities/createCommunity/createCommunity.saga.ts index 2c7768a47c..ebed5ee240 100644 --- a/packages/state-manager/src/sagas/communities/createCommunity/createCommunity.saga.ts +++ b/packages/state-manager/src/sagas/communities/createCommunity/createCommunity.saga.ts @@ -52,9 +52,6 @@ export function* createCommunitySaga( }) ) - // TODO: Community metadata should already exist on the backend after creating - // the community. - yield* put(communitiesActions.sendCommunityMetadata()) yield* put(publicChannelsActions.createGeneralChannel()) // TODO: We can likely refactor this a bit. Currently, we issue the owner's // certificate before creating the community, but then we add the owner's CSR diff --git a/packages/state-manager/src/sagas/communities/sendCommunityCaData/sendCommunityCaData.saga.ts b/packages/state-manager/src/sagas/communities/sendCommunityCaData/sendCommunityCaData.saga.ts deleted file mode 100644 index c0810903f3..0000000000 --- a/packages/state-manager/src/sagas/communities/sendCommunityCaData/sendCommunityCaData.saga.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { apply, select } from 'typed-redux-saga' -import { type PayloadAction } from '@reduxjs/toolkit' - -import { applyEmitParams, type Socket } from '../../../types' -import { communitiesSelectors } from '../communities.selectors' -import { type communitiesActions } from '../communities.slice' -import { type PermsData, SocketActionTypes } from '@quiet/types' - -export function* sendCommunityCaDataSaga( - socket: Socket, - _action: PayloadAction['payload']> -): Generator { - const community = yield* select(communitiesSelectors.currentCommunity) - - if (!community) { - console.error('Cannot send community metadata, no community') - return - } - - if (!community.CA) { - console.log('Cannot send community metadata, no CA in community') - return - } - - const payload: PermsData = { - certificate: community.CA.rootCertString, - privKey: community.CA.rootKeyString, - } - yield* apply(socket, socket.emit, applyEmitParams(SocketActionTypes.SET_COMMUNITY_CA_DATA, payload)) -} diff --git a/packages/state-manager/src/sagas/communities/updateCommunityMetadata/updateCommunityMetadata.saga.ts b/packages/state-manager/src/sagas/communities/updateCommunityMetadata/updateCommunityMetadata.saga.ts deleted file mode 100644 index b3efe67cde..0000000000 --- a/packages/state-manager/src/sagas/communities/updateCommunityMetadata/updateCommunityMetadata.saga.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { CommunityMetadata, SocketActionTypes } from '@quiet/types' -import { type PayloadAction } from '@reduxjs/toolkit' -import { apply, select, put, take } from 'typed-redux-saga' -import { channel } from 'redux-saga' -import { applyEmitParams, type Socket } from '../../../types' -import { communitiesSelectors } from '../communities.selectors' -import { communitiesActions } from '../communities.slice' -import { identitySelectors } from '../../identity/identity.selectors' - -export function* sendCommunityMetadataSaga( - socket: Socket, - _action: PayloadAction['payload']> -): Generator { - const identity = yield* select(identitySelectors.currentIdentity) - const community = yield* select(communitiesSelectors.currentCommunity) - - if (!identity?.userCertificate) { - console.error('Cannot send community metadata, no owner certificate') - return - } - - if (!community) { - console.error('Cannot send community metadata, no community') - return - } - - if (!community.rootCa || !community.CA) { - console.log('Cannot send community metadata, no rootCa or CA in community') - return - } - - const communityMetadataPayload: CommunityMetadata = { - id: community.id, - ownerCertificate: identity.userCertificate, - rootCa: community.rootCa, - } - - yield* apply( - socket, - socket.emitWithAck, - applyEmitParams(SocketActionTypes.SET_COMMUNITY_METADATA, communityMetadataPayload) - ) -} diff --git a/packages/state-manager/src/sagas/identity/identity.types.ts b/packages/state-manager/src/sagas/identity/identity.types.ts index 31420aa396..74819b8aee 100644 --- a/packages/state-manager/src/sagas/identity/identity.types.ts +++ b/packages/state-manager/src/sagas/identity/identity.types.ts @@ -51,29 +51,6 @@ export interface RegisterUserCertificatePayload { serviceAddress: string } -export interface PermsData { - certificate: string - privKey: string -} - -export interface RegisterOwnerCertificatePayload { - communityId: string - userCsr: UserCsr - permsData: PermsData -} - -export interface SaveCertificatePayload { - certificate: string - rootPermsData: PermsData -} - -export interface SaveOwnerCertificatePayload { - id: string - peerId: string - certificate: string - permsData: PermsData -} - export interface StoreUserCertificatePayload { userCertificate: string communityId: string diff --git a/packages/state-manager/src/sagas/network/network.slice.ts b/packages/state-manager/src/sagas/network/network.slice.ts index 7a491e4037..ace82febd3 100644 --- a/packages/state-manager/src/sagas/network/network.slice.ts +++ b/packages/state-manager/src/sagas/network/network.slice.ts @@ -14,7 +14,6 @@ export const networkSlice = createSlice({ name: StoreKeys.Network, reducers: { addInitializedCommunity: (state, action: PayloadAction) => { - console.log('Hunting for heisenbug: adding initialized community ', action.payload) state.initializedCommunities = { ...state.initializedCommunities, [action.payload]: true, diff --git a/packages/state-manager/src/sagas/socket/startConnection/startConnection.saga.ts b/packages/state-manager/src/sagas/socket/startConnection/startConnection.saga.ts index b900613ae6..9c908f13ee 100644 --- a/packages/state-manager/src/sagas/socket/startConnection/startConnection.saga.ts +++ b/packages/state-manager/src/sagas/socket/startConnection/startConnection.saga.ts @@ -84,8 +84,6 @@ export function subscribe(socket: Socket) { | ReturnType | ReturnType | ReturnType - | ReturnType - | ReturnType | ReturnType | ReturnType >(emit => { @@ -140,10 +138,6 @@ export function subscribe(socket: Socket) { // Community socket.on(SocketActionTypes.COMMUNITY_LAUNCHED, (payload: ResponseLaunchCommunityPayload) => { - console.log('Hunting for heisenbug: Community event received in state-manager') - // TODO: We can send this once when creating the community and - // store it in the backend. - emit(communitiesActions.sendCommunityCaData()) emit(filesActions.checkForMissingFiles(payload.id)) emit(networkActions.addInitializedCommunity(payload.id)) emit(communitiesActions.clearInvitationCodes()) diff --git a/packages/state-manager/src/types.ts b/packages/state-manager/src/types.ts index f0d3ba0499..44260530d5 100644 --- a/packages/state-manager/src/types.ts +++ b/packages/state-manager/src/types.ts @@ -50,11 +50,6 @@ export interface EmitEvents { [SocketActionTypes.LEAVE_COMMUNITY]: () => void [SocketActionTypes.CREATE_NETWORK]: EmitEvent void> [SocketActionTypes.ADD_CSR]: EmitEvent - [SocketActionTypes.SET_COMMUNITY_METADATA]: EmitEvent< - CommunityMetadata, - (response: CommunityMetadata | undefined) => void - > - [SocketActionTypes.SET_COMMUNITY_CA_DATA]: EmitEvent [SocketActionTypes.SET_USER_PROFILE]: EmitEvent [SocketActionTypes.LOAD_MIGRATION_DATA]: EmitEvent> } diff --git a/packages/types/src/socket.ts b/packages/types/src/socket.ts index 45495c829a..573fb1a313 100644 --- a/packages/types/src/socket.ts +++ b/packages/types/src/socket.ts @@ -21,8 +21,6 @@ export enum SocketActionTypes { CREATE_COMMUNITY = 'createCommunity', LAUNCH_COMMUNITY = 'launchCommunity', LEAVE_COMMUNITY = 'leaveCommunity', - SET_COMMUNITY_CA_DATA = 'setCommunityCaData', - SET_COMMUNITY_METADATA = 'setCommunityMetadata', // ====== Channels ====== From e4f945f58b9070eb7b9b30fce92c64ff47fcf7c1 Mon Sep 17 00:00:00 2001 From: Isla Koenigsknecht Date: Thu, 11 Apr 2024 22:16:35 -0400 Subject: [PATCH 08/48] Better peer sorting and updated initial diallng (#2427) * Better peer sorting and updated initial diallng * Update tests --- packages/backend/package-lock.json | 33 +++++++++ packages/backend/package.json | 1 + .../connections-manager.service.tor.spec.ts | 7 +- .../connections-manager.service.ts | 2 +- .../src/nest/libp2p/libp2p.service.spec.ts | 8 ++- .../nest/libp2p/process-in-chunks.service.ts | 69 +++++++++++++------ .../src/nest/libp2p/process-in-chunks.spec.ts | 6 +- .../src/nest/local-db/local-db.service.ts | 20 +++++- packages/common/src/sortPeers.ts | 35 ++++++---- 9 files changed, 134 insertions(+), 47 deletions(-) diff --git a/packages/backend/package-lock.json b/packages/backend/package-lock.json index 72b65f7167..2697f66df2 100644 --- a/packages/backend/package-lock.json +++ b/packages/backend/package-lock.json @@ -26,6 +26,7 @@ "dotenv": "8.2.0", "events": "^3.2.0", "express": "^4.17.1", + "fastq": "^1.17.1", "get-port": "^5.1.1", "go-ipfs": "npm:mocked-go-ipfs@0.17.0", "http-server": "^0.12.3", @@ -10816,6 +10817,23 @@ "node": ">= 4.9.1" } }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fastq/node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, "node_modules/fb-watchman": { "version": "2.0.1", "license": "Apache-2.0", @@ -30783,6 +30801,21 @@ "version": "1.0.16", "dev": true }, + "fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "requires": { + "reusify": "^1.0.4" + }, + "dependencies": { + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" + } + } + }, "fb-watchman": { "version": "2.0.1", "requires": { diff --git a/packages/backend/package.json b/packages/backend/package.json index 58e01cc76f..87312a5a6e 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -108,6 +108,7 @@ "dotenv": "8.2.0", "events": "^3.2.0", "express": "^4.17.1", + "fastq": "^1.17.1", "get-port": "^5.1.1", "go-ipfs": "npm:mocked-go-ipfs@0.17.0", "http-server": "^0.12.3", diff --git a/packages/backend/src/nest/connections-manager/connections-manager.service.tor.spec.ts b/packages/backend/src/nest/connections-manager/connections-manager.service.tor.spec.ts index fb41bf4aa2..b56b6d121a 100644 --- a/packages/backend/src/nest/connections-manager/connections-manager.service.tor.spec.ts +++ b/packages/backend/src/nest/connections-manager/connections-manager.service.tor.spec.ts @@ -31,6 +31,7 @@ import waitForExpect from 'wait-for-expect' import { Libp2pEvents } from '../libp2p/libp2p.types' import { sleep } from '../common/sleep' import { createLibp2pAddress } from '@quiet/common' +import { lib } from 'crypto-js' jest.setTimeout(100_000) @@ -121,7 +122,7 @@ afterEach(async () => { }) describe('Connections manager', () => { - it('saves peer stats when peer has been disconnected', async () => { + it.only('saves peer stats when peer has been disconnected', async () => { class RemotePeerEventDetail { peerId: string @@ -205,8 +206,8 @@ describe('Connections manager', () => { await sleep(5000) // It looks LibP2P dials peers initially when it's started and // then IPFS service dials peers again when started, thus - // peersCount * 2 - expect(spyOnDial).toHaveBeenCalledTimes(peersCount * 2) + // peersCount-1 * 2 because we don't dial ourself (the first peer in the list) + expect(spyOnDial).toHaveBeenCalledTimes((peersCount - 1) * 2) // Temporary fix for hanging test - websocketOverTor doesn't have abortController await sleep(5000) }) diff --git a/packages/backend/src/nest/connections-manager/connections-manager.service.ts b/packages/backend/src/nest/connections-manager/connections-manager.service.ts index 07353b6fa0..f52de0f6bb 100644 --- a/packages/backend/src/nest/connections-manager/connections-manager.service.ts +++ b/packages/backend/src/nest/connections-manager/connections-manager.service.ts @@ -517,7 +517,7 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI agent: this.socksProxyAgent, localAddress: this.libp2pService.createLibp2pAddress(onionAddress, peerId.toString()), targetPort: this.ports.libp2pHiddenService, - peers: peers ?? [], + peers: peers ? peers.slice(1) : [], psk: Libp2pService.generateLibp2pPSK(community.psk).fullKey, } await this.libp2pService.createInstance(params) diff --git a/packages/backend/src/nest/libp2p/libp2p.service.spec.ts b/packages/backend/src/nest/libp2p/libp2p.service.spec.ts index 13ebb5f1ed..91d9d177c4 100644 --- a/packages/backend/src/nest/libp2p/libp2p.service.spec.ts +++ b/packages/backend/src/nest/libp2p/libp2p.service.spec.ts @@ -8,7 +8,7 @@ import { Libp2pEvents, Libp2pNodeParams } from './libp2p.types' import { toString as uint8ArrayToString } from 'uint8arrays/to-string' import validator from 'validator' import waitForExpect from 'wait-for-expect' -import { ProcessInChunksService } from './process-in-chunks.service' +import { DEFAULT_NUM_TRIES, ProcessInChunksService } from './process-in-chunks.service' describe('Libp2pService', () => { let module: TestingModule @@ -93,10 +93,12 @@ describe('Libp2pService', () => { await libp2pService.createInstance(params) expect(libp2pService.libp2pInstance).not.toBeNull() // @ts-expect-error processItem is private - const dialPeerSpy = jest.spyOn(processInChunks, 'processItem') + const processItemSpy = jest.spyOn(processInChunks, 'processItem') + const dialSpy = jest.spyOn(libp2pService.libp2pInstance!, 'dial') libp2pService.emit(Libp2pEvents.DIAL_PEERS, addresses) await waitForExpect(async () => { - expect(dialPeerSpy).toBeCalledTimes(1) + expect(processItemSpy).toBeCalledTimes(2 * DEFAULT_NUM_TRIES) + expect(dialSpy).toBeCalledTimes(1) }) }) }) diff --git a/packages/backend/src/nest/libp2p/process-in-chunks.service.ts b/packages/backend/src/nest/libp2p/process-in-chunks.service.ts index 2e7b30588b..e57e681227 100644 --- a/packages/backend/src/nest/libp2p/process-in-chunks.service.ts +++ b/packages/backend/src/nest/libp2p/process-in-chunks.service.ts @@ -1,12 +1,22 @@ import { EventEmitter } from 'events' +import fastq from 'fastq' +import type { queue, done } from 'fastq' + import Logger from '../common/logger' const DEFAULT_CHUNK_SIZE = 10 +export const DEFAULT_NUM_TRIES = 2 + +type ProcessTask = { + data: T + tries: number +} export class ProcessInChunksService extends EventEmitter { private isActive: boolean - private data: T[] + private data: Set = new Set() private chunkSize: number + private taskQueue: queue> private processItem: (arg: T) => Promise private readonly logger = Logger(ProcessInChunksService.name) constructor() { @@ -14,43 +24,62 @@ export class ProcessInChunksService extends EventEmitter { } public init(data: T[], processItem: (arg: T) => Promise, chunkSize: number = DEFAULT_CHUNK_SIZE) { - this.data = data + this.logger(`Initializing process-in-chunks.service with peers ${JSON.stringify(data, null, 2)}`) this.processItem = processItem this.chunkSize = chunkSize + this.taskQueue = fastq(this, this.processOneItem, this.chunkSize) + this.updateData(data) + this.addToTaskQueue() } - updateData(items: T[]) { + public updateData(items: T[]) { this.logger(`Updating data with ${items.length} items`) - this.data = [...new Set(this.data.concat(items))] + this.taskQueue.pause() + items.forEach(item => this.data.add(item)) + this.addToTaskQueue() } - public async processOneItem() { - const toProcess = this.data.shift() - if (toProcess) { - try { - await this.processItem(toProcess) - } catch (e) { - this.logger(`Processing ${toProcess} failed, message:`, e.message) - } finally { - process.nextTick(async () => { - await this.processOneItem() - }) + private addToTaskQueue() { + this.logger(`Adding ${this.data.size} items to the task queue`) + for (const item of this.data) { + if (item) { + this.logger(`Adding data ${item} to the task queue`) + this.data.delete(item) + try { + this.taskQueue.push({ data: item, tries: 0 } as ProcessTask) + } catch (e) { + this.logger.error(`Error occurred while adding new task for item ${item} to the queue`, e) + this.data.add(item) + } } } } - public async process() { - this.logger(`Processing ${this.data.length} items`) - for (let i = 0; i < this.chunkSize; i++) { - // Do not wait for this promise as items should be processed simultineously - void this.processOneItem() + public async processOneItem(task: ProcessTask) { + try { + this.logger(`Processing task with data ${task.data}`) + await this.processItem(task.data) + } catch (e) { + this.logger.error(`Processing task with data ${task.data} failed`, e) + if (task.tries + 1 < DEFAULT_NUM_TRIES) { + this.logger(`Will try to re-attempt task with data ${task.data}`) + this.taskQueue.push({ ...task, tries: task.tries + 1 }) + } + } finally { + this.logger(`Done attempting to process task with data ${task.data}`) } } + public async process() { + this.logger(`Processing ${this.taskQueue.length} items`) + this.taskQueue.resume() + } + public stop() { if (this.isActive) { this.logger('Stopping initial dial') this.isActive = false + this.taskQueue.pause() } } } diff --git a/packages/backend/src/nest/libp2p/process-in-chunks.spec.ts b/packages/backend/src/nest/libp2p/process-in-chunks.spec.ts index 38979c1cf9..ce751afdf3 100644 --- a/packages/backend/src/nest/libp2p/process-in-chunks.spec.ts +++ b/packages/backend/src/nest/libp2p/process-in-chunks.spec.ts @@ -27,7 +27,7 @@ describe('ProcessInChunks', () => { processInChunks.init(['a', 'b', 'c', 'd'], mockProcessItem) await processInChunks.process() await waitForExpect(() => { - expect(mockProcessItem).toBeCalledTimes(4) + expect(mockProcessItem).toBeCalledTimes(6) }) }) @@ -43,7 +43,7 @@ describe('ProcessInChunks', () => { processInChunks.updateData(['e', 'f']) await processInChunks.process() await waitForExpect(() => { - expect(mockProcessItem).toBeCalledTimes(4) + expect(mockProcessItem).toBeCalledTimes(5) }) }) @@ -60,7 +60,7 @@ describe('ProcessInChunks', () => { processInChunks.init(['a', 'b', 'c', 'd'], mockProcessItem, chunkSize) await processInChunks.process() await waitForExpect(() => { - expect(mockProcessItem).toBeCalledTimes(4) + expect(mockProcessItem).toBeCalledTimes(2) }) }) diff --git a/packages/backend/src/nest/local-db/local-db.service.ts b/packages/backend/src/nest/local-db/local-db.service.ts index 6c85552ac8..fd36ed80d8 100644 --- a/packages/backend/src/nest/local-db/local-db.service.ts +++ b/packages/backend/src/nest/local-db/local-db.service.ts @@ -95,7 +95,21 @@ export class LocalDbService { } } - public async getSortedPeers(peers: string[] = []): Promise { + public async getSortedPeers( + peers?: string[] | undefined, + includeLocalPeerAddress: boolean = true + ): Promise { + if (!peers) { + const currentCommunity = await this.getCurrentCommunity() + if (!currentCommunity) { + throw new Error('No peers were provided and no community was found to extract peers from') + } + peers = currentCommunity.peerList + if (!peers) { + throw new Error('No peers provided and no peers found on current stored community') + } + } + const peersStats = (await this.get(LocalDBKeys.PEERS)) || {} const stats: NetworkStats[] = Object.values(peersStats) const network = await this.getNetworkInfo() @@ -103,9 +117,9 @@ export class LocalDbService { if (network) { const localPeerAddress = createLibp2pAddress(network.hiddenService.onionAddress, network.peerId.id) this.logger('Local peer', localPeerAddress) - return filterAndSortPeers(peers, stats, localPeerAddress) + return filterAndSortPeers(peers, stats, localPeerAddress, includeLocalPeerAddress) } else { - return filterAndSortPeers(peers, stats) + return filterAndSortPeers(peers, stats, undefined, includeLocalPeerAddress) } } diff --git a/packages/common/src/sortPeers.ts b/packages/common/src/sortPeers.ts index 682111c1f2..634a79cfaf 100644 --- a/packages/common/src/sortPeers.ts +++ b/packages/common/src/sortPeers.ts @@ -1,5 +1,4 @@ import { type NetworkStats } from '@quiet/types' -import { isDefined } from './helpers' import { filterValidAddresses } from './libp2p' /** @@ -14,9 +13,11 @@ This is the very simple algorithm for evaluating the most wanted peers. export const filterAndSortPeers = ( peersAddresses: string[], stats: NetworkStats[], - localPeerAddress?: string + localPeerAddress?: string, + includeLocalPeerAddress: boolean = true ): string[] => { peersAddresses = filterValidAddresses(peersAddresses) + const currentlyConnected = [...stats].filter(peer => peer.connectionTime === 0) const lastSeenSorted = [...stats].sort((a, b) => { return b.lastSeen - a.lastSeen }) @@ -24,7 +25,7 @@ export const filterAndSortPeers = ( return b.connectionTime - a.connectionTime }) - const mostWantedPeers: NetworkStats[] = [] + const mostWantedPeers: NetworkStats[] = currentlyConnected for (let i = 0; i < stats.length; i++) { const peerOne = lastSeenSorted[i] @@ -39,22 +40,28 @@ export const filterAndSortPeers = ( } } - const peerList = mostWantedPeers.map(peerId => { - return peersAddresses.find(peerAddress => { + const peerSet: Set = new Set() + if (includeLocalPeerAddress && localPeerAddress) { + peerSet.add(localPeerAddress) + } + + mostWantedPeers.forEach(peer => { + const found = peersAddresses.find(peerAddress => { const id = peerAddress.split('/')[7] - if (id === peerId.peerId) { + if (id === peer.peerId) { peersAddresses.splice(peersAddresses.indexOf(peerAddress), 1) return true } }) + if (found && found !== '') { + peerSet.add(found) + } + }) + peersAddresses.forEach(peerAddress => { + if (!peerSet.has(peerAddress)) { + peerSet.add(peerAddress) + } }) - return [ - ...new Set([ - localPeerAddress, // Set local peer as first - ...peerList.concat(peersAddresses), - ]), - ] - .filter(address => address !== null && address !== '') - .filter(isDefined) + return [...peerSet] } From a7ca0dfc5a053c5766596933c4931a7c193f424d Mon Sep 17 00:00:00 2001 From: Isla Koenigsknecht Date: Fri, 12 Apr 2024 08:19:43 -0400 Subject: [PATCH 09/48] Remove only (#2429) --- .../connections-manager/connections-manager.service.tor.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/nest/connections-manager/connections-manager.service.tor.spec.ts b/packages/backend/src/nest/connections-manager/connections-manager.service.tor.spec.ts index b56b6d121a..98d717762b 100644 --- a/packages/backend/src/nest/connections-manager/connections-manager.service.tor.spec.ts +++ b/packages/backend/src/nest/connections-manager/connections-manager.service.tor.spec.ts @@ -122,7 +122,7 @@ afterEach(async () => { }) describe('Connections manager', () => { - it.only('saves peer stats when peer has been disconnected', async () => { + it('saves peer stats when peer has been disconnected', async () => { class RemotePeerEventDetail { peerId: string From 3929124c792dcfdba47fd04e5e6915bf885cd7ae Mon Sep 17 00:00:00 2001 From: Lucas Leblow Date: Fri, 12 Apr 2024 08:44:29 -0700 Subject: [PATCH 10/48] Publish - @quiet/desktop@2.2.0-alpha.1 - @quiet/mobile@2.2.0-alpha.1 --- packages/desktop/CHANGELOG.md | 17 ++++++++++++++++- packages/desktop/package-lock.json | 4 ++-- packages/desktop/package.json | 2 +- packages/mobile/CHANGELOG.md | 18 +++++++++++++++++- packages/mobile/android/app/build.gradle | 4 ++-- packages/mobile/ios/Quiet/Info.plist | 2 +- packages/mobile/ios/QuietTests/Info.plist | 2 +- packages/mobile/package-lock.json | 4 ++-- packages/mobile/package.json | 2 +- 9 files changed, 43 insertions(+), 12 deletions(-) diff --git a/packages/desktop/CHANGELOG.md b/packages/desktop/CHANGELOG.md index 59c0741d0f..a0647f6d73 100644 --- a/packages/desktop/CHANGELOG.md +++ b/packages/desktop/CHANGELOG.md @@ -1,3 +1,19 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +# [2.2.0-alpha.1](/compare/@quiet/desktop@2.2.0-alpha.0...@quiet/desktop@2.2.0-alpha.1) (2024-04-12) + + +### Bug Fixes + +* Remove unused dmPublicKey to prevent UI delay during joining (#2392) c089c68, closes #2392 + + + + + [unreleased] * Refactored package.json to have consistent license "GPL-3.0-or-later" @@ -274,4 +290,3 @@ * C4 for Quiet architecture. Context and Container diagrams. * Invite tab as default in settings - diff --git a/packages/desktop/package-lock.json b/packages/desktop/package-lock.json index 4294790b37..c9e049edb0 100644 --- a/packages/desktop/package-lock.json +++ b/packages/desktop/package-lock.json @@ -1,12 +1,12 @@ { "name": "@quiet/desktop", - "version": "2.2.0-alpha.0", + "version": "2.2.0-alpha.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@quiet/desktop", - "version": "2.2.0-alpha.0", + "version": "2.2.0-alpha.1", "license": "GPL-3.0-or-later", "dependencies": { "@electron/remote": "^2.0.8", diff --git a/packages/desktop/package.json b/packages/desktop/package.json index 0784f8c19a..f8bb9b22ca 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -80,7 +80,7 @@ }, "homepage": "https://github.com/TryQuiet", "@comment version": "To build new version for specific platform, just replace platform in version tag to one of following linux, mac, windows", - "version": "2.2.0-alpha.0", + "version": "2.2.0-alpha.1", "description": "Decentralized team chat", "main": "dist/main/main.js", "scripts": { diff --git a/packages/mobile/CHANGELOG.md b/packages/mobile/CHANGELOG.md index 59c0741d0f..1eff753a00 100644 --- a/packages/mobile/CHANGELOG.md +++ b/packages/mobile/CHANGELOG.md @@ -1,3 +1,20 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +# [2.2.0-alpha.1](/compare/@quiet/mobile@2.2.0-alpha.0...@quiet/mobile@2.2.0-alpha.1) (2024-04-12) + + +### Bug Fixes + +* Adds a quick fix for the iOS sync issue after suspend (#2414) 595d896, closes #2414 +* Remove unused dmPublicKey to prevent UI delay during joining (#2392) c089c68, closes #2392 + + + + + [unreleased] * Refactored package.json to have consistent license "GPL-3.0-or-later" @@ -274,4 +291,3 @@ * C4 for Quiet architecture. Context and Container diagrams. * Invite tab as default in settings - diff --git a/packages/mobile/android/app/build.gradle b/packages/mobile/android/app/build.gradle index 021ad4583c..d74a8bbe60 100644 --- a/packages/mobile/android/app/build.gradle +++ b/packages/mobile/android/app/build.gradle @@ -192,8 +192,8 @@ android { applicationId "com.quietmobile" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 412 - versionName "2.2.0-alpha.0" + versionCode 413 + versionName "2.2.0-alpha.1" resValue "string", "build_config_package", "com.quietmobile" testBuildType System.getProperty('testBuildType', 'debug') testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' diff --git a/packages/mobile/ios/Quiet/Info.plist b/packages/mobile/ios/Quiet/Info.plist index 1140a0beac..01a2cc8c61 100644 --- a/packages/mobile/ios/Quiet/Info.plist +++ b/packages/mobile/ios/Quiet/Info.plist @@ -34,7 +34,7 @@ CFBundleVersion - 369 + 370 ITSAppUsesNonExemptEncryption LSRequiresIPhoneOS diff --git a/packages/mobile/ios/QuietTests/Info.plist b/packages/mobile/ios/QuietTests/Info.plist index 7f5a67eb0a..167b0126a2 100644 --- a/packages/mobile/ios/QuietTests/Info.plist +++ b/packages/mobile/ios/QuietTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 369 + 370 diff --git a/packages/mobile/package-lock.json b/packages/mobile/package-lock.json index 210e059f3d..5f0c2a9a1a 100644 --- a/packages/mobile/package-lock.json +++ b/packages/mobile/package-lock.json @@ -1,12 +1,12 @@ { "name": "@quiet/mobile", - "version": "2.2.0-alpha.0", + "version": "2.2.0-alpha.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@quiet/mobile", - "version": "2.2.0-alpha.0", + "version": "2.2.0-alpha.1", "dependencies": { "@peculiar/webcrypto": "^1.4.3", "@react-native-clipboard/clipboard": "^1.13.2", diff --git a/packages/mobile/package.json b/packages/mobile/package.json index b29f0f248d..3f052532e6 100644 --- a/packages/mobile/package.json +++ b/packages/mobile/package.json @@ -1,6 +1,6 @@ { "name": "@quiet/mobile", - "version": "2.2.0-alpha.0", + "version": "2.2.0-alpha.1", "scripts": { "build": "tsc -p tsconfig.build.json --noEmit", "storybook-android": "ENVFILE=.env.storybook react-native run-android --mode=storybookDebug --appIdSuffix=storybook.debug", From ef0e2c62af0fb2c4016951188aeff55a22d94221 Mon Sep 17 00:00:00 2001 From: Lucas Leblow Date: Fri, 12 Apr 2024 08:44:34 -0700 Subject: [PATCH 11/48] Update packages CHANGELOG.md --- packages/desktop/CHANGELOG.md | 30 +++++++++++------------------- packages/mobile/CHANGELOG.md | 31 +++++++++++-------------------- 2 files changed, 22 insertions(+), 39 deletions(-) diff --git a/packages/desktop/CHANGELOG.md b/packages/desktop/CHANGELOG.md index a0647f6d73..e5ad80e5c2 100644 --- a/packages/desktop/CHANGELOG.md +++ b/packages/desktop/CHANGELOG.md @@ -1,35 +1,27 @@ -# Change Log - -All notable changes to this project will be documented in this file. -See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - -# [2.2.0-alpha.1](/compare/@quiet/desktop@2.2.0-alpha.0...@quiet/desktop@2.2.0-alpha.1) (2024-04-12) - - -### Bug Fixes +[unreleased] -* Remove unused dmPublicKey to prevent UI delay during joining (#2392) c089c68, closes #2392 +# New features: +# Refactorings: +# Fixes: +[2.2.0] +# New features: -[unreleased] +* Add utilities for emoji detection in messages and make all-emoji message larger font size ([#519](https://github.com/TryQuiet/quiet/issues/519)) -* Refactored package.json to have consistent license "GPL-3.0-or-later" - # Refactorings: * Use ack for CREATE_NETWORK and simplify +* Move Community model to the backend -# Fixes +# Fixes: * Allow JPEG and GIF files as profile photos ([#2332](https://github.com/TryQuiet/quiet/issues/2332)) -* Fixes issues with recreating general channel when deleted while offline ([#2334](https://github.com/TryQuiet/quiet/issues/2334)) - -# New features - -* Add utilities for emoji detection in messages and make all-emoji message larger font size ([#519](https://github.com/TryQuiet/quiet/issues/519)) +* Fix issues with recreating general channel when deleted while offline ([#2334](https://github.com/TryQuiet/quiet/issues/2334)) +* Fix package.json license inconsistency [2.1.2] diff --git a/packages/mobile/CHANGELOG.md b/packages/mobile/CHANGELOG.md index 1eff753a00..e5ad80e5c2 100644 --- a/packages/mobile/CHANGELOG.md +++ b/packages/mobile/CHANGELOG.md @@ -1,36 +1,27 @@ -# Change Log - -All notable changes to this project will be documented in this file. -See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - -# [2.2.0-alpha.1](/compare/@quiet/mobile@2.2.0-alpha.0...@quiet/mobile@2.2.0-alpha.1) (2024-04-12) - - -### Bug Fixes +[unreleased] -* Adds a quick fix for the iOS sync issue after suspend (#2414) 595d896, closes #2414 -* Remove unused dmPublicKey to prevent UI delay during joining (#2392) c089c68, closes #2392 +# New features: +# Refactorings: +# Fixes: +[2.2.0] +# New features: -[unreleased] +* Add utilities for emoji detection in messages and make all-emoji message larger font size ([#519](https://github.com/TryQuiet/quiet/issues/519)) -* Refactored package.json to have consistent license "GPL-3.0-or-later" - # Refactorings: * Use ack for CREATE_NETWORK and simplify +* Move Community model to the backend -# Fixes +# Fixes: * Allow JPEG and GIF files as profile photos ([#2332](https://github.com/TryQuiet/quiet/issues/2332)) -* Fixes issues with recreating general channel when deleted while offline ([#2334](https://github.com/TryQuiet/quiet/issues/2334)) - -# New features - -* Add utilities for emoji detection in messages and make all-emoji message larger font size ([#519](https://github.com/TryQuiet/quiet/issues/519)) +* Fix issues with recreating general channel when deleted while offline ([#2334](https://github.com/TryQuiet/quiet/issues/2334)) +* Fix package.json license inconsistency [2.1.2] From 2aa2e3e7f90af8f1516775d7f9278c5a0ea54d6f Mon Sep 17 00:00:00 2001 From: Lucas Leblow Date: Mon, 15 Apr 2024 13:35:48 -0700 Subject: [PATCH 12/48] fix: Wait for community data in registerUsernameSaga (#2444) --- .../identity/registerUsername/registerUsername.saga.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/state-manager/src/sagas/identity/registerUsername/registerUsername.saga.ts b/packages/state-manager/src/sagas/identity/registerUsername/registerUsername.saga.ts index 4622539f7f..ae91990777 100644 --- a/packages/state-manager/src/sagas/identity/registerUsername/registerUsername.saga.ts +++ b/packages/state-manager/src/sagas/identity/registerUsername/registerUsername.saga.ts @@ -5,6 +5,7 @@ import { identitySelectors } from '../identity.selectors' import { identityActions } from '../identity.slice' import { config } from '../../users/const/certFieldTypes' import { Socket } from '../../../types' +import { communitiesActions } from '../../communities/communities.slice' import { communitiesSelectors } from '../../communities/communities.selectors' import { CreateUserCsrPayload, RegisterCertificatePayload, Community } from '@quiet/types' @@ -16,7 +17,13 @@ export function* registerUsernameSaga( const { nickname, isUsernameTaken = false } = action.payload - const community = yield* select(communitiesSelectors.currentCommunity) + let community = yield* select(communitiesSelectors.currentCommunity) + + if (!community) { + yield* take(communitiesActions.addNewCommunity) + } + + community = yield* select(communitiesSelectors.currentCommunity) if (!community) { console.error('Could not register username, no community data') From 9f04bdfa6e5160d2c50ebfaa2ee0d6ce33d2b174 Mon Sep 17 00:00:00 2001 From: Lucas Leblow Date: Mon, 15 Apr 2024 19:45:14 -0700 Subject: [PATCH 13/48] fix: Wait for channels to be initialized before sending messages (#2448) --- .../messages/sendMessage/sendMessage.saga.ts | 16 ++++++++++++++- .../sendIntroductionMessage.saga.ts | 20 +------------------ 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/packages/state-manager/src/sagas/messages/sendMessage/sendMessage.saga.ts b/packages/state-manager/src/sagas/messages/sendMessage/sendMessage.saga.ts index 9bb7c39b45..1297c1ea5b 100644 --- a/packages/state-manager/src/sagas/messages/sendMessage/sendMessage.saga.ts +++ b/packages/state-manager/src/sagas/messages/sendMessage/sendMessage.saga.ts @@ -1,7 +1,7 @@ import { type Socket, applyEmitParams } from '../../../types' import { type PayloadAction } from '@reduxjs/toolkit' import { sign, loadPrivateKey, pubKeyFromCsr } from '@quiet/identity' -import { call, select, apply, put } from 'typed-redux-saga' +import { call, select, apply, put, delay } from 'typed-redux-saga' import { arrayBufferToString } from 'pvutils' import { config } from '../../users/const/certFieldTypes' import { identitySelectors } from '../../identity/identity.selectors' @@ -75,6 +75,20 @@ export function* sendMessageSaga( const isUploadingFileMessage = action.payload.media?.cid?.includes('uploading') if (isUploadingFileMessage) return // Do not broadcast message until file is uploaded + // Wait until we have subscribed to the channel + // + // TODO: I think we probably want to revise how we are sending + // messages by having the backend handling queueing and retrying + // (in a durable way). + while (true) { + const subscribedChannels = yield* select(publicChannelsSelectors.subscribedChannels) + if (subscribedChannels.includes(channelId)) { + break + } + console.error('Failed to send message, channel not subscribed. Retrying...') + yield* delay(500) + } + yield* apply( socket, socket.emit, diff --git a/packages/state-manager/src/sagas/publicChannels/sendIntroductionMessage/sendIntroductionMessage.saga.ts b/packages/state-manager/src/sagas/publicChannels/sendIntroductionMessage/sendIntroductionMessage.saga.ts index 87e2cd04f4..8a485494b4 100644 --- a/packages/state-manager/src/sagas/publicChannels/sendIntroductionMessage/sendIntroductionMessage.saga.ts +++ b/packages/state-manager/src/sagas/publicChannels/sendIntroductionMessage/sendIntroductionMessage.saga.ts @@ -1,4 +1,4 @@ -import { put, select, call, delay } from 'typed-redux-saga' +import { put, select, call } from 'typed-redux-saga' import { messagesActions } from '../../messages/messages.slice' import { publicChannelsSelectors } from '../publicChannels.selectors' import { WriteMessagePayload, MessageType, PublicChannel, PublicChannelStorage } from '@quiet/types' @@ -24,24 +24,6 @@ export function* sendIntroductionMessageSaga(): Generator { channelId: generalChannel.id, } - // FIXME: This is a quick fix for an issue that can be fixed by - // unifying CHANNELS_STORED and CHANNELS_SUBSCRIBED events and - // refactoring a bit. The problem is that the frontend sends a - // message upon receiving the CHANNELS_STORED event, but the channel - // hasn't been fully initialized/subscribed yet (it doesn't exist in - // publicChannelsRepos on the backend so the backend fails to send - // it). Ideally, I think we should only tell the frontend about - // channels once they've been fully initialized. Once we fix that, - // we can remove the following code. - while (true) { - const subscribedChannels = yield* select(publicChannelsSelectors.subscribedChannels) - if (subscribedChannels.includes(generalChannel.id)) { - break - } - console.error('Failed to send introduction message, general channel not subscribed. Retrying...') - yield* delay(500) - } - yield* put(messagesActions.sendMessage(payload)) yield* put(identityActions.updateIdentity({ ...identity, introMessageSent: true })) } From b3eb28173077588e5b2f22ab83abc9e6b0209adf Mon Sep 17 00:00:00 2001 From: Lucas Leblow Date: Mon, 15 Apr 2024 19:47:54 -0700 Subject: [PATCH 14/48] Publish - @quiet/desktop@2.2.0-alpha.2 - @quiet/mobile@2.2.0-alpha.2 --- packages/desktop/CHANGELOG.md | 13 +++++++++++++ packages/desktop/package-lock.json | 4 ++-- packages/desktop/package.json | 2 +- packages/mobile/CHANGELOG.md | 13 +++++++++++++ packages/mobile/android/app/build.gradle | 4 ++-- packages/mobile/ios/Quiet/Info.plist | 2 +- packages/mobile/ios/QuietTests/Info.plist | 2 +- packages/mobile/package-lock.json | 4 ++-- packages/mobile/package.json | 2 +- 9 files changed, 36 insertions(+), 10 deletions(-) diff --git a/packages/desktop/CHANGELOG.md b/packages/desktop/CHANGELOG.md index e5ad80e5c2..d42471006a 100644 --- a/packages/desktop/CHANGELOG.md +++ b/packages/desktop/CHANGELOG.md @@ -1,3 +1,16 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +# [2.2.0-alpha.2](https://github.com/TryQuiet/quiet/compare/@quiet/desktop@2.2.0-alpha.1...@quiet/desktop@2.2.0-alpha.2) (2024-04-16) + +**Note:** Version bump only for package @quiet/desktop + + + + + [unreleased] # New features: diff --git a/packages/desktop/package-lock.json b/packages/desktop/package-lock.json index c9e049edb0..8c5ca79ed2 100644 --- a/packages/desktop/package-lock.json +++ b/packages/desktop/package-lock.json @@ -1,12 +1,12 @@ { "name": "@quiet/desktop", - "version": "2.2.0-alpha.1", + "version": "2.2.0-alpha.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@quiet/desktop", - "version": "2.2.0-alpha.1", + "version": "2.2.0-alpha.2", "license": "GPL-3.0-or-later", "dependencies": { "@electron/remote": "^2.0.8", diff --git a/packages/desktop/package.json b/packages/desktop/package.json index f8bb9b22ca..303b5cbb7b 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -80,7 +80,7 @@ }, "homepage": "https://github.com/TryQuiet", "@comment version": "To build new version for specific platform, just replace platform in version tag to one of following linux, mac, windows", - "version": "2.2.0-alpha.1", + "version": "2.2.0-alpha.2", "description": "Decentralized team chat", "main": "dist/main/main.js", "scripts": { diff --git a/packages/mobile/CHANGELOG.md b/packages/mobile/CHANGELOG.md index e5ad80e5c2..20a7127c9a 100644 --- a/packages/mobile/CHANGELOG.md +++ b/packages/mobile/CHANGELOG.md @@ -1,3 +1,16 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +# [2.2.0-alpha.2](https://github.com/TryQuiet/quiet/compare/@quiet/mobile@2.2.0-alpha.1...@quiet/mobile@2.2.0-alpha.2) (2024-04-16) + +**Note:** Version bump only for package @quiet/mobile + + + + + [unreleased] # New features: diff --git a/packages/mobile/android/app/build.gradle b/packages/mobile/android/app/build.gradle index d74a8bbe60..6449017724 100644 --- a/packages/mobile/android/app/build.gradle +++ b/packages/mobile/android/app/build.gradle @@ -192,8 +192,8 @@ android { applicationId "com.quietmobile" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 413 - versionName "2.2.0-alpha.1" + versionCode 414 + versionName "2.2.0-alpha.2" resValue "string", "build_config_package", "com.quietmobile" testBuildType System.getProperty('testBuildType', 'debug') testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' diff --git a/packages/mobile/ios/Quiet/Info.plist b/packages/mobile/ios/Quiet/Info.plist index 01a2cc8c61..51846dee44 100644 --- a/packages/mobile/ios/Quiet/Info.plist +++ b/packages/mobile/ios/Quiet/Info.plist @@ -34,7 +34,7 @@ CFBundleVersion - 370 + 371 ITSAppUsesNonExemptEncryption LSRequiresIPhoneOS diff --git a/packages/mobile/ios/QuietTests/Info.plist b/packages/mobile/ios/QuietTests/Info.plist index 167b0126a2..d450792c6a 100644 --- a/packages/mobile/ios/QuietTests/Info.plist +++ b/packages/mobile/ios/QuietTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 370 + 371 diff --git a/packages/mobile/package-lock.json b/packages/mobile/package-lock.json index 5f0c2a9a1a..706228ed3c 100644 --- a/packages/mobile/package-lock.json +++ b/packages/mobile/package-lock.json @@ -1,12 +1,12 @@ { "name": "@quiet/mobile", - "version": "2.2.0-alpha.1", + "version": "2.2.0-alpha.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@quiet/mobile", - "version": "2.2.0-alpha.1", + "version": "2.2.0-alpha.2", "dependencies": { "@peculiar/webcrypto": "^1.4.3", "@react-native-clipboard/clipboard": "^1.13.2", diff --git a/packages/mobile/package.json b/packages/mobile/package.json index 3f052532e6..2d8395a009 100644 --- a/packages/mobile/package.json +++ b/packages/mobile/package.json @@ -1,6 +1,6 @@ { "name": "@quiet/mobile", - "version": "2.2.0-alpha.1", + "version": "2.2.0-alpha.2", "scripts": { "build": "tsc -p tsconfig.build.json --noEmit", "storybook-android": "ENVFILE=.env.storybook react-native run-android --mode=storybookDebug --appIdSuffix=storybook.debug", From ce027b2f054974a24e306e5efdd4e381a0602f6f Mon Sep 17 00:00:00 2001 From: Lucas Leblow Date: Mon, 15 Apr 2024 19:47:58 -0700 Subject: [PATCH 15/48] Update packages CHANGELOG.md --- packages/desktop/CHANGELOG.md | 13 ------------- packages/mobile/CHANGELOG.md | 13 ------------- 2 files changed, 26 deletions(-) diff --git a/packages/desktop/CHANGELOG.md b/packages/desktop/CHANGELOG.md index d42471006a..e5ad80e5c2 100644 --- a/packages/desktop/CHANGELOG.md +++ b/packages/desktop/CHANGELOG.md @@ -1,16 +1,3 @@ -# Change Log - -All notable changes to this project will be documented in this file. -See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - -# [2.2.0-alpha.2](https://github.com/TryQuiet/quiet/compare/@quiet/desktop@2.2.0-alpha.1...@quiet/desktop@2.2.0-alpha.2) (2024-04-16) - -**Note:** Version bump only for package @quiet/desktop - - - - - [unreleased] # New features: diff --git a/packages/mobile/CHANGELOG.md b/packages/mobile/CHANGELOG.md index 20a7127c9a..e5ad80e5c2 100644 --- a/packages/mobile/CHANGELOG.md +++ b/packages/mobile/CHANGELOG.md @@ -1,16 +1,3 @@ -# Change Log - -All notable changes to this project will be documented in this file. -See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - -# [2.2.0-alpha.2](https://github.com/TryQuiet/quiet/compare/@quiet/mobile@2.2.0-alpha.1...@quiet/mobile@2.2.0-alpha.2) (2024-04-16) - -**Note:** Version bump only for package @quiet/mobile - - - - - [unreleased] # New features: From 0966478d056f9d3149ebd05f9013cc94683ca7d9 Mon Sep 17 00:00:00 2001 From: Isla Koenigsknecht Date: Thu, 18 Apr 2024 17:59:00 -0400 Subject: [PATCH 16/48] Fix(2321): No Duplicate CSRs (#2460) * Fix issue of duplicate CRSs being generated * Install LFS with checkout to avoid random errors --- .github/workflows/e2e-android.yml | 7 ++----- .github/workflows/e2e-ios.yml | 7 ++----- CHANGELOG.md | 2 ++ .../certifacteRequests/certificatesRequestsStore.ts | 1 + packages/backend/src/nest/storage/storage.service.ts | 2 +- 5 files changed, 8 insertions(+), 11 deletions(-) diff --git a/.github/workflows/e2e-android.yml b/.github/workflows/e2e-android.yml index 4481bdc54d..60ad8b4502 100644 --- a/.github/workflows/e2e-android.yml +++ b/.github/workflows/e2e-android.yml @@ -15,17 +15,14 @@ jobs: steps: - uses: actions/checkout@v4 + with: + lfs: true - name: Install dependencies run: | npm i npm run lerna bootstrap --scope @quiet/eslint-config,@quiet/logger,@quiet/common,@quiet/types,@quiet/state-manager,@quiet/backend,@quiet/identity,@quiet/mobile,backend-bundle - - name: Pull binaries - run: | - git lfs install - git lfs pull - - name: Pass local config run : | cat << EOF >> packages/mobile/android/local.properties diff --git a/.github/workflows/e2e-ios.yml b/.github/workflows/e2e-ios.yml index 34ad277d79..77a89d425b 100644 --- a/.github/workflows/e2e-ios.yml +++ b/.github/workflows/e2e-ios.yml @@ -14,17 +14,14 @@ jobs: steps: - uses: actions/checkout@v4 + with: + lfs: true - name: Install dependencies run: | npm i npm run lerna bootstrap --scope @quiet/eslint-config,@quiet/logger,@quiet/common,@quiet/types,@quiet/state-manager,@quiet/backend,@quiet/identity,@quiet/mobile,backend-bundle - - name: Pull binaries - run: | - git lfs install - git lfs pull - - name: Install pods run: | cd packages/mobile/ios diff --git a/CHANGELOG.md b/CHANGELOG.md index e5ad80e5c2..dcc0e8492c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ # Fixes: +* Don't create duplicate CSRs when joining a community under certain circumstances ([#2321](https://github.com/TryQuiet/quiet/issues/2321)) + [2.2.0] # New features: diff --git a/packages/backend/src/nest/storage/certifacteRequests/certificatesRequestsStore.ts b/packages/backend/src/nest/storage/certifacteRequests/certificatesRequestsStore.ts index 5c30871d59..ce32cbe387 100644 --- a/packages/backend/src/nest/storage/certifacteRequests/certificatesRequestsStore.ts +++ b/packages/backend/src/nest/storage/certifacteRequests/certificatesRequestsStore.ts @@ -29,6 +29,7 @@ export class CertificatesRequestsStore extends EventEmitter { write: ['*'], }, }) + await this.store.load() this.store.events.on('write', async (_address, entry) => { this.logger('Added CSR to database') diff --git a/packages/backend/src/nest/storage/storage.service.ts b/packages/backend/src/nest/storage/storage.service.ts index 1d9ba7eafe..5f353a3119 100644 --- a/packages/backend/src/nest/storage/storage.service.ts +++ b/packages/backend/src/nest/storage/storage.service.ts @@ -314,7 +314,7 @@ export class StorageService extends EventEmitter { public async updatePeersList() { const users = this.getAllUsers() - const peers = users.map(peer => createLibp2pAddress(peer.onionAddress, peer.peerId)) + const peers = Array.from(new Set(users.map(peer => createLibp2pAddress(peer.onionAddress, peer.peerId)))) console.log('updatePeersList, peers count:', peers.length) const community = await this.localDbService.getCurrentCommunity() From 87ed5651371d350ac1030c055f68a1d42145600e Mon Sep 17 00:00:00 2001 From: Wiktor Sieprawski Date: Fri, 19 Apr 2024 11:59:32 +0200 Subject: [PATCH 17/48] fix: postpone restore connection saga (#2462) --- .../startConnection/restoreConnection/restoreConnection.saga.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mobile/src/store/init/startConnection/restoreConnection/restoreConnection.saga.ts b/packages/mobile/src/store/init/startConnection/restoreConnection/restoreConnection.saga.ts index 7b7195b2be..3a7c001360 100644 --- a/packages/mobile/src/store/init/startConnection/restoreConnection/restoreConnection.saga.ts +++ b/packages/mobile/src/store/init/startConnection/restoreConnection/restoreConnection.saga.ts @@ -2,7 +2,7 @@ import { delay, put, select } from 'typed-redux-saga' import { initSelectors } from '../../init.selectors' import { initActions } from '../../init.slice' -const WEBSOCKET_CONNECTION_DELAY = 5000 +const WEBSOCKET_CONNECTION_DELAY = 15000 export function* restoreConnectionSaga(): Generator { // Give the worker time to init websocket connection From e2f0204b2cf2cdb6755b12218eabccbbef793438 Mon Sep 17 00:00:00 2001 From: Wiktor Sieprawski Date: Fri, 19 Apr 2024 12:03:10 +0200 Subject: [PATCH 18/48] fix: change ci emulator name configuration (#2463) --- packages/mobile/.detoxrc.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mobile/.detoxrc.js b/packages/mobile/.detoxrc.js index a03e86f3a9..8ac7d9d0ce 100644 --- a/packages/mobile/.detoxrc.js +++ b/packages/mobile/.detoxrc.js @@ -77,7 +77,7 @@ module.exports = { emulator_ci: { type: 'android.emulator', device: { - avdName: 'Pixel_7_API_31', + avdName: 'emulator_ci', }, }, }, From 2af0531cbce8539addaef0fec8f3deb8baea9ff8 Mon Sep 17 00:00:00 2001 From: Lucas Leblow Date: Mon, 29 Apr 2024 07:58:05 -0600 Subject: [PATCH 19/48] fix: Various fixes related to peers, CSRs and backend startup (#2455) Fixes for the following issues: - Peers can be deleted if CSRs don't sync - Backend starting before the frontend is ready, resulting in missed events - Adding duplicate CSRs --- .../connections-manager.service.ts | 3 +- .../src/nest/local-db/local-db.service.ts | 18 +-- .../registration/registration.functions.ts | 1 - .../nest/registration/registration.service.ts | 13 +- .../backend/src/nest/socket/socket.service.ts | 14 +- .../certificatesRequestsStore.ts | 16 +- .../certificates/certificates.store.spec.ts | 3 - .../certificates/certificates.store.ts | 6 +- .../src/nest/storage/storage.service.spec.ts | 49 +++++- .../src/nest/storage/storage.service.ts | 91 ++++++++--- .../src/renderer/sagas/socket/socket.saga.ts | 8 +- .../src/rtl-tests/channel.main.test.tsx | 1 + .../src/rtl-tests/community.create.test.tsx | 2 +- .../src/rtl-tests/community.join.test.tsx | 3 +- packages/identity/src/extractPubKey.ts | 1 + .../src/integrationTests/appActions.ts | 3 +- .../src/screens/Channel/Channel.screen.tsx | 2 +- .../startConnection/startConnection.saga.ts | 7 +- .../createCommunity/createCommunity.saga.ts | 10 +- .../launchCommunity/launchCommunity.saga.ts | 5 + .../checkLocalCsr/checkLocalCsr.saga.test.ts | 103 ------------ .../checkLocalCsr/checkLocalCsr.saga.ts | 45 ------ .../sagas/identity/identity.master.saga.ts | 4 - .../src/sagas/identity/identity.slice.ts | 5 +- .../registerCertificate.saga.test.ts | 146 ------------------ .../registerCertificate.saga.ts | 34 ---- .../registerUsername.saga.test.ts | 8 +- .../registerUsername/registerUsername.saga.ts | 26 +++- .../identity/saveUserCsr/saveUserCsr.saga.ts | 2 + .../src/sagas/messages/messages.slice.ts | 15 +- .../messages/sendMessage/sendMessage.saga.ts | 46 +++--- .../publicChannels.selectors.ts | 2 + .../startConnection/startConnection.saga.ts | 3 - .../src/sagas/users/users.selectors.ts | 2 +- packages/state-manager/src/types.ts | 1 + packages/types/src/socket.ts | 6 + 36 files changed, 256 insertions(+), 448 deletions(-) delete mode 100644 packages/state-manager/src/sagas/identity/checkLocalCsr/checkLocalCsr.saga.test.ts delete mode 100644 packages/state-manager/src/sagas/identity/checkLocalCsr/checkLocalCsr.saga.ts delete mode 100644 packages/state-manager/src/sagas/identity/registerCertificate/registerCertificate.saga.test.ts delete mode 100644 packages/state-manager/src/sagas/identity/registerCertificate/registerCertificate.saga.ts diff --git a/packages/backend/src/nest/connections-manager/connections-manager.service.ts b/packages/backend/src/nest/connections-manager/connections-manager.service.ts index f52de0f6bb..6d0e047dc7 100644 --- a/packages/backend/src/nest/connections-manager/connections-manager.service.ts +++ b/packages/backend/src/nest/connections-manager/connections-manager.service.ts @@ -209,7 +209,7 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI const network = await this.localDbService.getNetworkInfo() if (community && network) { - const sortedPeers = await this.localDbService.getSortedPeers(community.peerList) + const sortedPeers = await this.localDbService.getSortedPeers(community.peerList ?? []) this.logger('launchCommunityFromStorage - sorted peers', sortedPeers) if (sortedPeers.length > 0) { community.peerList = sortedPeers @@ -517,6 +517,7 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI agent: this.socksProxyAgent, localAddress: this.libp2pService.createLibp2pAddress(onionAddress, peerId.toString()), targetPort: this.ports.libp2pHiddenService, + // Ignore local address peers: peers ? peers.slice(1) : [], psk: Libp2pService.generateLibp2pPSK(community.psk).fullKey, } diff --git a/packages/backend/src/nest/local-db/local-db.service.ts b/packages/backend/src/nest/local-db/local-db.service.ts index fd36ed80d8..b9df332a20 100644 --- a/packages/backend/src/nest/local-db/local-db.service.ts +++ b/packages/backend/src/nest/local-db/local-db.service.ts @@ -95,21 +95,9 @@ export class LocalDbService { } } - public async getSortedPeers( - peers?: string[] | undefined, - includeLocalPeerAddress: boolean = true - ): Promise { - if (!peers) { - const currentCommunity = await this.getCurrentCommunity() - if (!currentCommunity) { - throw new Error('No peers were provided and no community was found to extract peers from') - } - peers = currentCommunity.peerList - if (!peers) { - throw new Error('No peers provided and no peers found on current stored community') - } - } - + // I think we can move this into StorageService (keep this service + // focused on CRUD). + public async getSortedPeers(peers: string[], includeLocalPeerAddress: boolean = true): Promise { const peersStats = (await this.get(LocalDBKeys.PEERS)) || {} const stats: NetworkStats[] = Object.values(peersStats) const network = await this.getNetworkInfo() diff --git a/packages/backend/src/nest/registration/registration.functions.ts b/packages/backend/src/nest/registration/registration.functions.ts index 3eea07d353..16ddf4d6af 100644 --- a/packages/backend/src/nest/registration/registration.functions.ts +++ b/packages/backend/src/nest/registration/registration.functions.ts @@ -64,7 +64,6 @@ export const extractPendingCsrs = async (payload: { csrs: string[]; certificates pendingCsrs.push(csr) } } - logger('DuplicatedCertBug', { parsedUniqueCsrs, pendingNames, certNames }) return pendingCsrs } diff --git a/packages/backend/src/nest/registration/registration.service.ts b/packages/backend/src/nest/registration/registration.service.ts index 9c04e2bb56..17cd8ff838 100644 --- a/packages/backend/src/nest/registration/registration.service.ts +++ b/packages/backend/src/nest/registration/registration.service.ts @@ -44,14 +44,14 @@ export class RegistrationService extends EventEmitter implements OnModuleInit { } public async tryIssueCertificates() { - this.logger('Trying to issue certificates', this.registrationEventInProgress, this.registrationEvents) + this.logger('Trying to process registration event') // Process only a single registration event at a time so that we // do not register two certificates with the same name. if (!this.registrationEventInProgress) { // Get the next event. const event = this.registrationEvents.shift() if (event) { - this.logger('Issuing certificates', event) + this.logger('Processing registration event', event) // Event processing in progress this.registrationEventInProgress = true @@ -62,7 +62,7 @@ export class RegistrationService extends EventEmitter implements OnModuleInit { certificates: (await this.storageService?.loadAllCertificates()) as string[], }) - this.logger('Finished issuing certificates') + this.logger('Finished processing registration event') // Event processing finished this.registrationEventInProgress = false @@ -71,6 +71,8 @@ export class RegistrationService extends EventEmitter implements OnModuleInit { setTimeout(this.tryIssueCertificates.bind(this), 0) } } + } else { + this.logger('Registration event processing already in progress') } } @@ -90,14 +92,14 @@ export class RegistrationService extends EventEmitter implements OnModuleInit { return } - this.logger('DuplicatedCertBug', { payload }) const pendingCsrs = await extractPendingCsrs(payload) - this.logger('DuplicatedCertBug', { pendingCsrs }) + this.logger(`Issuing certificates`) await Promise.all( pendingCsrs.map(async csr => { await this.registerUserCertificate(csr) }) ) + this.logger('Total certificates issued:', pendingCsrs.length) } // TODO: This doesn't save the owner's certificate in OrbitDB, so perhaps we @@ -121,7 +123,6 @@ export class RegistrationService extends EventEmitter implements OnModuleInit { public async registerUserCertificate(csr: string): Promise { const result = await issueCertificate(csr, this.permsData) - this.logger('DuplicatedCertBug', { result }) if (result?.cert) { // Save certificate (awaited) so that we are sure that the certs // are saved before processing the next round of CSRs. diff --git a/packages/backend/src/nest/socket/socket.service.ts b/packages/backend/src/nest/socket/socket.service.ts index d126eb3be1..10b9797491 100644 --- a/packages/backend/src/nest/socket/socket.service.ts +++ b/packages/backend/src/nest/socket/socket.service.ts @@ -47,36 +47,40 @@ export class SocketService extends EventEmitter implements OnModuleInit { } async onModuleInit() { - this.logger('init:started') + this.logger('init: Started') this.attachListeners() await this.init() - this.logger('init:finished') + this.logger('init: Finished') } public async init() { const connection = new Promise(resolve => { this.serverIoProvider.io.on(SocketActionTypes.CONNECTION, socket => { - this.logger('init: connection') - resolve() + socket.on(SocketActionTypes.START, async () => { + resolve() + }) }) }) await this.listen() + this.logger('init: Waiting for frontend to connect') await connection + this.logger('init: Frontend connected') } private readonly attachListeners = (): void => { // Attach listeners here this.serverIoProvider.io.on(SocketActionTypes.CONNECTION, socket => { - this.logger('socket connection') + this.logger('Socket connection') // On websocket connection, update presentation service with network data this.emit(SocketActionTypes.CONNECTION) socket.on(SocketActionTypes.CLOSE, async () => { + this.logger('Socket connection closed') this.emit(SocketActionTypes.CLOSE) }) diff --git a/packages/backend/src/nest/storage/certifacteRequests/certificatesRequestsStore.ts b/packages/backend/src/nest/storage/certifacteRequests/certificatesRequestsStore.ts index ce32cbe387..be2d6c54b5 100644 --- a/packages/backend/src/nest/storage/certifacteRequests/certificatesRequestsStore.ts +++ b/packages/backend/src/nest/storage/certifacteRequests/certificatesRequestsStore.ts @@ -29,7 +29,6 @@ export class CertificatesRequestsStore extends EventEmitter { write: ['*'], }, }) - await this.store.load() this.store.events.on('write', async (_address, entry) => { this.logger('Added CSR to database') @@ -41,8 +40,8 @@ export class CertificatesRequestsStore extends EventEmitter { this.loadedCertificateRequests() }) - // TODO: Load CSRs in case the owner closes the app before issuing - // certificates + // @ts-ignore + await this.store.load({ fetchEntryTimeout: 15000 }) this.logger('Initialized') } @@ -77,7 +76,7 @@ export class CertificatesRequestsStore extends EventEmitter { await parsedCsr.verify() await this.validateCsrFormat(csr) } catch (err) { - console.error('Failed to validate user csr:', csr, err?.message) + console.error('Failed to validate user CSR:', csr, err?.message) return false } return true @@ -100,15 +99,13 @@ export class CertificatesRequestsStore extends EventEmitter { .map(e => { return e.payload.value }) + this.logger('Total CSRs:', allEntries.length) - this.logger('DuplicatedCertBug', { allEntries }) const allCsrsUnique = [...new Set(allEntries)] - this.logger('DuplicatedCertBug', { allCsrsUnique }) await Promise.all( allCsrsUnique .filter(async csr => { const validation = await CertificatesRequestsStore.validateUserCsr(csr) - this.logger('DuplicatedCertBug', { validation, csr }) if (validation) return true return false }) @@ -122,8 +119,9 @@ export class CertificatesRequestsStore extends EventEmitter { filteredCsrsMap.set(pubKey, csr) }) ) - this.logger('DuplicatedCertBug', '[...filteredCsrsMap.values()]', [...filteredCsrsMap.values()]) - return [...filteredCsrsMap.values()] + const validCsrs = [...filteredCsrsMap.values()] + this.logger('Valid CSRs:', validCsrs.length) + return validCsrs } public clean() { diff --git a/packages/backend/src/nest/storage/certificates/certificates.store.spec.ts b/packages/backend/src/nest/storage/certificates/certificates.store.spec.ts index 104c1a19bc..bf0a1b1d54 100644 --- a/packages/backend/src/nest/storage/certificates/certificates.store.spec.ts +++ b/packages/backend/src/nest/storage/certificates/certificates.store.spec.ts @@ -135,7 +135,6 @@ describe('CertificatesStore', () => { await certificatesStore.addCertificate(certificate) - // @ts-expect-error - getCertificates is protected const certificates = await certificatesStore.getCertificates() expect(certificates).toContain(certificate) @@ -149,7 +148,6 @@ describe('CertificatesStore', () => { await certificatesStore.addCertificate(certificate) - // @ts-expect-error - getCertificates is protected const certificates = await certificatesStore.getCertificates() expect(certificates).not.toContain(certificate) @@ -161,7 +159,6 @@ describe('CertificatesStore', () => { certificatesStore.updateMetadata(communityMetadata) - // @ts-expect-error - getCertificates is protected jest.spyOn(certificatesStore, 'getCertificates').mockResolvedValue([certificate1, certificate2]) const certificates = await certificatesStore.loadAllCertificates() diff --git a/packages/backend/src/nest/storage/certificates/certificates.store.ts b/packages/backend/src/nest/storage/certificates/certificates.store.ts index 66fb655ffc..6341d51d0e 100644 --- a/packages/backend/src/nest/storage/certificates/certificates.store.ts +++ b/packages/backend/src/nest/storage/certificates/certificates.store.ts @@ -146,7 +146,7 @@ export class CertificatesStore extends EventEmitter { * as specified in the comment section of * https://github.com/TryQuiet/quiet/issues/1899 */ - protected async getCertificates() { + public async getCertificates(): Promise { if (!this.store) { return [] } @@ -166,7 +166,6 @@ export class CertificatesStore extends EventEmitter { } const validation = await this.validateCertificate(certificate) - this.logger('DuplicatedCertBug', { validation, certificate }) if (validation) { const parsedCertificate = parseCertificate(certificate) const pubkey = keyFromCertificate(parsedCertificate) @@ -190,7 +189,8 @@ export class CertificatesStore extends EventEmitter { const validCerts = validCertificates.filter(i => i != undefined) this.logger(`Valid certificates: ${validCerts.length}`) - return validCerts + // TODO: Why doesn't TS infer this properly? + return validCerts as string[] } public async getCertificateUsername(pubkey: string) { diff --git a/packages/backend/src/nest/storage/storage.service.spec.ts b/packages/backend/src/nest/storage/storage.service.spec.ts index fa65bcd37b..54f3e9e284 100644 --- a/packages/backend/src/nest/storage/storage.service.spec.ts +++ b/packages/backend/src/nest/storage/storage.service.spec.ts @@ -511,17 +511,52 @@ describe('StorageService', () => { describe('Users', () => { it('gets all users from db', async () => { - await storageService.init(peerId) - const mockGetCsrs = jest.fn() - // @ts-ignore - Property 'getAllEventLogEntries' is protected - storageService.getAllEventLogEntries = mockGetCsrs - mockGetCsrs.mockReturnValue([ + const certs = [ + // b + 'MIICITCCAcegAwIBAgIGAY8GkBEVMAoGCCqGSM49BAMCMAwxCjAIBgNVBAMTAWEwHhcNMjQwNDIyMTYwNzM1WhcNMzAwMjAxMDcwMDAwWjBJMUcwRQYDVQQDEz56Z2hpZGV4czdxdDI0aXZ1M2pvYmpxZHR6end0eWF1NGxwcG51bng1cGtpZjc2cGtweXA3cWNpZC5vbmlvbjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABDG8SNnoS1BYoV72jcyQFVlsrwvd2Bb9/9L13Tc4SHJwitTUB3F+y/7pk0tAPrZi2qasU2PO9lTwUxXYcAfpCRSjgdcwgdQwCQYDVR0TBAIwADALBgNVHQ8EBAMCAIAwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMBEGCisGAQQBg4wbAgEEAxMBYjA9BgkrBgECAQ8DAQEEMBMuUW1lUGJCMjVoMWZYN1dBRk42ckZSNGFWRFdVRlFNU3RSSEdERFM0UlFaUTRZcTBJBgNVHREEQjBAgj56Z2hpZGV4czdxdDI0aXZ1M2pvYmpxZHR6end0eWF1NGxwcG51bng1cGtpZjc2cGtweXA3cWNpZC5vbmlvbjAKBggqhkjOPQQDAgNIADBFAiBkTZo6/D0YgNMPcDpuf7n+rDEQls6cMVxEVw/H8vxbhwIhAM+e6we9YP4JeNgOGgd0iZNEpq8N7dla4XO+YVWrh0YG', + + // c + 'MIICITCCAcegAwIBAgIGAY8Glf+pMAoGCCqGSM49BAMCMAwxCjAIBgNVBAMTAWEwHhcNMjQwNDIyMTYxNDA0WhcNMzAwMjAxMDcwMDAwWjBJMUcwRQYDVQQDEz5uaGxpdWpuNjZlMzQ2ZXZ2dnhlYWY0cW1hN3Bxc2hjZ2J1NnQ3d3Nlb2FubmMyZnk0Y25zY3J5ZC5vbmlvbjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABP1WBKQdMz5yMpv5hWj6j+auIsnfiJE8dtuxeeM4N03K1An61F0o47CWg04DydwmoPn5gwefEv8t9Cz9nv/VUGejgdcwgdQwCQYDVR0TBAIwADALBgNVHQ8EBAMCAIAwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMBEGCisGAQQBg4wbAgEEAxMBYzA9BgkrBgECAQ8DAQEEMBMuUW1WY1hRTXVmRWNZS0R0d3NFSlRIUGJzc3BCeU02U0hUYlJHR2VEdkVFdU1RQTBJBgNVHREEQjBAgj5uaGxpdWpuNjZlMzQ2ZXZ2dnhlYWY0cW1hN3Bxc2hjZ2J1NnQ3d3Nlb2FubmMyZnk0Y25zY3J5ZC5vbmlvbjAKBggqhkjOPQQDAgNIADBFAiEAgMCBxF3oK4ituEWcAK6uawMCludZu4YujIpBIR+v2LICIBhMHXrBy1KWc70t6idB+5XkInsRZz5nw1vwgRJ4mw98', + ] + + const csrs = [ + // c + 'MIIB4TCCAYgCAQAwSTFHMEUGA1UEAxM+emdoaWRleHM3cXQyNGl2dTNqb2JqcWR0enp3dHlhdTRscHBudW54NXBraWY3NnBrcHlwN3FjaWQub25pb24wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQxvEjZ6EtQWKFe9o3MkBVZbK8L3dgW/f/S9d03OEhycIrU1Adxfsv+6ZNLQD62YtqmrFNjzvZU8FMV2HAH6QkUoIHcMC4GCSqGSIb3DQEJDjEhMB8wHQYDVR0OBBYEFG1W6vJTK/uPuRK2LPaVZyebVVc+MA8GCSqGSIb3DQEJDDECBAAwEQYKKwYBBAGDjBsCATEDEwFiMD0GCSsGAQIBDwMBATEwEy5RbWVQYkIyNWgxZlg3V0FGTjZyRlI0YVZEV1VGUU1TdFJIR0REUzRSUVpRNFlxMEcGA1UdETFAEz56Z2hpZGV4czdxdDI0aXZ1M2pvYmpxZHR6end0eWF1NGxwcG51bng1cGtpZjc2cGtweXA3cWNpZC5vbmlvbjAKBggqhkjOPQQDAgNHADBEAiAjxneoJZtCzkd75HTT+pcj+objG3S04omjeMMw1N+B/wIgAaJRgifnWEnWFYm614UmPw9un2Uwk1gVhN2tSwJ65sM=', + + // o 'MIIDHjCCAsMCAQAwSTFHMEUGA1UEAxM+NnZ1MmJ4a2k3NzdpdDNjcGF5djZmcTZ2cGw0a2Uza3pqN2d4aWNmeWdtNTVkaGh0cGh5ZmR2eWQub25pb24wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATMpfp2hSfWFL26OZlZKZEWG9fyAM1ndlEzO0kLxT0pA/7/fs+a5X/s4TkzqCVVQSzhas/84q0WE99ScAcM1LQJoIICFjAuBgkqhkiG9w0BCQ4xITAfMB0GA1UdDgQWBBR6VRzktP1pzZxsGUaJivNUrtgSrzCCAUcGCSqGSIb3DQEJDDGCATgEggE0KZq9s6HEViRfplVgYkulg6XV411ZRe4U1UjfXTf1pRaygfcenGbT6RRagPtZzjuq5hHdYhqDjRzZhnbn8ZASYTgBM7qcseUq5UpS1pE08DI2jePKqatp3Pzm6a/MGSziESnREx784JlKfwKMjJl33UA8lQm9nhSeAIHyBx3c4Lf8IXdW2n3rnhbVfjpBMAxwh6lt+e5agtGXy+q/xAESUeLPfUgRYWctlLgt8Op+WTpLyBkZsVFoBvJrMt2XdM0RI32YzTRr56GXFa4VyQmY5xXwlQSPgidAP7jPkVygNcoeXvAz2ZCk3IR1Cn3mX8nMko53MlDNaMYldUQA0ug28/S7BlSlaq2CDD4Ol3swTq7C4KGTxKrI36ruYUZx7NEaQDF5V7VvqPCZ0fZoTIJuSYTQ67gwEQYKKwYBBAGDjBsCATEDEwFvMD0GCSsGAQIBDwMBATEwEy5RbVhSWTRyaEF4OE11cThkTUdrcjlxa25KZEU2VUhaRGRHYURSVFFFYndGTjViMEcGA1UdETFAEz42dnUyYnhraTc3N2l0M2NwYXl2NmZxNnZwbDRrZTNremo3Z3hpY2Z5Z201NWRoaHRwaHlmZHZ5ZC5vbmlvbjAKBggqhkjOPQQDAgNJADBGAiEAt+f1u/bchg5AZHv6NTGNoXeejTRWUhX3ioGwW6TGg84CIQCHqKNzDh2JjS/hUHx5PApAmfNnQTSf19X6LnNHQweU1g==', + + // o 'MIIDHTCCAsMCAQAwSTFHMEUGA1UEAxM+eTd5Y3ptdWdsMnRla2FtaTdzYmR6NXBmYWVtdng3YmFod3RocmR2Y2J6dzV2ZXgyY3JzcjI2cWQub25pb24wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATMq0l4bCmjdb0grtzpwtDVLM9E1IQpL9vrB4+lD9OBZzlrx2365jV7shVu9utas8w8fxtKoBZSnT5+32ZMFTB4oIICFjAuBgkqhkiG9w0BCQ4xITAfMB0GA1UdDgQWBBSoDQpTZdEvi1/Rr/muVXT1clyKRDCCAUcGCSqGSIb3DQEJDDGCATgEggE0BQvyvkiiXEf/PLKnsR1Ba9AhYsVO8o56bnftUnoVzBlRZgUzLJvOSroPk/EmbVz+okhMrcYNgCWHvxrAqHVVq0JRP6bi98BtCUotx6OPFHp5K5QCL60hod1uAnhKocyJG9tsoM9aS+krn/k+g4RCBjiPZ25cC7QG/UNr6wyIQ8elBho4MKm8iOp7EShSsZOV1f6xrnXYCC/zyUc85GEuycLzVImgAQvPATbdMzY4zSGnNLHxkvSUNxaR9LnEWf+i1jeqcOiXOvmdyU5Be3ZqhGKvvBg/5vyLQiCIfeapjZemnLqFHQBitglDm2xnKL6HzMyfZoAHPV7YcWYR4spU9Ju8Q8aqSeAryx7sx55eSR4GO5UQTo5DrQn6xtkwOZ/ytsOknFthF8jcA9uTAMDKA2TylCUwEQYKKwYBBAGDjBsCATEDEwFvMD0GCSsGAQIBDwMBATEwEy5RbVQxOFV2blVCa3NlTWMzU3FuZlB4cEh3TjhuekxySmVOU0xadGM4ckFGWGh6MEcGA1UdETFAEz55N3ljem11Z2wydGVrYW1pN3NiZHo1cGZhZW12eDdiYWh3dGhyZHZjYnp3NXZleDJjcnNyMjZxZC5vbmlvbjAKBggqhkjOPQQDAgNIADBFAiEAoFrAglxmk7ciD6AHQOB1qEoLu0NARcxgwmIry8oeTHwCICyXp5NJQ9Z8vReIAQNng2H2+/XjHifZEWzhoN0VkcBx', - ]) - const allUsers = storageService.getAllUsers() + ] + + await storageService.init(peerId) + // @ts-ignore + storageService.certificatesRequestsStore = { + getCsrs: jest.fn(() => { + return csrs + }), + } + // @ts-ignore + storageService.certificatesStore = { + getCertificates: jest.fn(() => { + return certs + }), + } + + const allUsers = await storageService.getAllUsers() expect(allUsers).toStrictEqual([ + { + onionAddress: 'zghidexs7qt24ivu3jobjqdtzzwtyau4lppnunx5pkif76pkpyp7qcid.onion', + peerId: 'QmePbB25h1fX7WAFN6rFR4aVDWUFQMStRHGDDS4RQZQ4Yq', + username: 'b', + }, + { + onionAddress: 'nhliujn66e346evvvxeaf4qma7pqshcgbu6t7wseoannc2fy4cnscryd.onion', + peerId: 'QmVcXQMufEcYKDtwsEJTHPbsspByM6SHTbRGGeDvEEuMQA', + username: 'c', + }, { onionAddress: '6vu2bxki777it3cpayv6fq6vpl4ke3kzj7gxicfygm55dhhtphyfdvyd.onion', peerId: 'QmXRY4rhAx8Muq8dMGkr9qknJdE6UHZDdGaDRTQEbwFN5b', diff --git a/packages/backend/src/nest/storage/storage.service.ts b/packages/backend/src/nest/storage/storage.service.ts index 5f353a3119..7a8c1d95fb 100644 --- a/packages/backend/src/nest/storage/storage.service.ts +++ b/packages/backend/src/nest/storage/storage.service.ts @@ -7,6 +7,7 @@ import { parseCertificationRequest, getCertFieldValue, getReqFieldValue, + keyFromCertificate, } from '@quiet/identity' import type { IPFS } from 'ipfs-core' import EventStore from 'orbit-db-eventstore' @@ -287,11 +288,15 @@ export class StorageService extends EventEmitter { this.certificatesStore.on(StorageEvents.CERTIFICATES_STORED, async payload => { this.emit(StorageEvents.CERTIFICATES_STORED, payload) await this.updatePeersList() + // TODO: Shouldn't we also dial new peers or at least add them + // to the peer store for the auto-dialer to handle? }) this.certificatesRequestsStore.on(StorageEvents.CSRS_STORED, async (payload: { csrs: string[] }) => { this.emit(StorageEvents.CSRS_STORED, payload) await this.updatePeersList() + // TODO: Shouldn't we also dial new peers or at least add them + // to the peer store for the auto-dialer to handle? }) this.communityMetadataStore.on(StorageEvents.COMMUNITY_METADATA_STORED, (meta: CommunityMetadata) => { @@ -313,18 +318,30 @@ export class StorageService extends EventEmitter { } public async updatePeersList() { - const users = this.getAllUsers() - const peers = Array.from(new Set(users.map(peer => createLibp2pAddress(peer.onionAddress, peer.peerId)))) - console.log('updatePeersList, peers count:', peers.length) - const community = await this.localDbService.getCurrentCommunity() - if (!community) return + if (!community) { + throw new Error('Failed to update peers list - community missing') + } + + // Always include existing peers. Otherwise, if CSRs or + // certificates do not replicate, then this could remove peers. + const existingPeers = community.peerList ?? [] + this.logger('Existing peers count:', existingPeers.length) + const users = await this.getAllUsers() + const peers = Array.from( + new Set([...existingPeers, ...users.map(user => createLibp2pAddress(user.onionAddress, user.peerId))]) + ) const sortedPeers = await this.localDbService.getSortedPeers(peers) - if (sortedPeers.length > 0) { - community.peerList = sortedPeers - await this.localDbService.setCommunity(community) + + // This should never happen, but just in case + if (sortedPeers.length === 0) { + throw new Error('Failed to update peers list - no peers') } + + this.logger('Updating community peer list. Peers count:', sortedPeers.length) + community.peerList = sortedPeers + await this.localDbService.setCommunity(community) this.emit(StorageEvents.COMMUNITY_UPDATED, community) } @@ -729,18 +746,56 @@ export class StorageService extends EventEmitter { return result } - public getAllUsers(): UserData[] { - const csrs = this.getAllEventLogEntries(this.certificatesRequestsStore.store) - this.logger('csrs count:', csrs.length) - const allUsers: UserData[] = [] + /** + * Retrieve all users (using certificates and CSRs to determine users) + */ + public async getAllUsers(): Promise { + const csrs = await this.certificatesRequestsStore.getCsrs() + const certs = await this.certificatesStore.getCertificates() + const allUsersByKey: Record = {} + + this.logger(`Retrieving all users. CSRs count: ${csrs.length} Certificates count: ${certs.length}`) + + for (const cert of certs) { + const parsedCert = parseCertificate(cert) + const pubKey = keyFromCertificate(parsedCert) + const onionAddress = getCertFieldValue(parsedCert, CertFieldsTypes.commonName) + const peerId = getCertFieldValue(parsedCert, CertFieldsTypes.peerId) + const username = getCertFieldValue(parsedCert, CertFieldsTypes.nickName) + + // TODO: This validation should go in CertificatesStore + if (!pubKey || !onionAddress || !peerId || !username) { + this.logger.error( + `Received invalid certificate. onionAddress: ${onionAddress} peerId: ${peerId} username: ${username}` + ) + continue + } + + allUsersByKey[pubKey] = { onionAddress, peerId, username } + } + for (const csr of csrs) { - const parsedCert = parseCertificationRequest(csr) - const onionAddress = getReqFieldValue(parsedCert, CertFieldsTypes.commonName) - const peerId = getReqFieldValue(parsedCert, CertFieldsTypes.peerId) - const username = getReqFieldValue(parsedCert, CertFieldsTypes.nickName) - if (!onionAddress || !peerId || !username) continue - allUsers.push({ onionAddress, peerId, username }) + const parsedCsr = parseCertificationRequest(csr) + const pubKey = keyFromCertificate(parsedCsr) + const onionAddress = getReqFieldValue(parsedCsr, CertFieldsTypes.commonName) + const peerId = getReqFieldValue(parsedCsr, CertFieldsTypes.peerId) + const username = getReqFieldValue(parsedCsr, CertFieldsTypes.nickName) + + // TODO: This validation should go in CertificatesRequestsStore + if (!pubKey || !onionAddress || !peerId || !username) { + this.logger.error(`Received invalid CSR. onionAddres: ${onionAddress} peerId: ${peerId} username: ${username}`) + continue + } + + if (!(pubKey in allUsersByKey)) { + allUsersByKey[pubKey] = { onionAddress, peerId, username } + } } + + const allUsers = Object.values(allUsersByKey) + + this.logger(`All users count: ${allUsers.length}`) + return allUsers } diff --git a/packages/desktop/src/renderer/sagas/socket/socket.saga.ts b/packages/desktop/src/renderer/sagas/socket/socket.saga.ts index beaec5d543..2268578ac9 100644 --- a/packages/desktop/src/renderer/sagas/socket/socket.saga.ts +++ b/packages/desktop/src/renderer/sagas/socket/socket.saga.ts @@ -1,5 +1,5 @@ import { io } from 'socket.io-client' -import { all, fork, takeEvery, call, put, cancel, FixedTask, select, take } from 'typed-redux-saga' +import { all, fork, takeEvery, call, put, cancel, FixedTask, select, take, delay, apply } from 'typed-redux-saga' import { PayloadAction } from '@reduxjs/toolkit' import { socket as stateManager, messages, connection, Socket } from '@quiet/state-manager' import { socketActions } from './socket.slice' @@ -7,6 +7,7 @@ import { eventChannel } from 'redux-saga' import { displayMessageNotificationSaga } from '../notifications/notifications.saga' import logger from '../../logger' import { encodeSecret } from '@quiet/common' +import { SocketActionTypes } from '@quiet/types' const log = logger('socket') @@ -27,6 +28,7 @@ export function* startConnectionSaga( if (!socketIOSecret) return + log('Connecting to backend') const token = encodeSecret(socketIOSecret) const socket = yield* call(io, `http://127.0.0.1:${dataPort}`, { withCredentials: true, @@ -43,6 +45,10 @@ export function* startConnectionSaga( function* setConnectedSaga(socket: Socket): Generator { const root = yield* fork(stateManager.useIO, socket) const observers = yield* fork(initObservers) + + console.log('Frontend is ready. Starting backend...') + yield* apply(socket, socket.emit, [SocketActionTypes.START]) + // Handle suspending current connection yield all([ takeEvery(socketActions.suspendConnection, cancelRootSaga, root), diff --git a/packages/desktop/src/rtl-tests/channel.main.test.tsx b/packages/desktop/src/rtl-tests/channel.main.test.tsx index 924111e61b..ee96a7dbfc 100644 --- a/packages/desktop/src/rtl-tests/channel.main.test.tsx +++ b/packages/desktop/src/rtl-tests/channel.main.test.tsx @@ -1029,6 +1029,7 @@ describe('Channel', () => { "Messages/lazyLoading", "Messages/resetCurrentPublicChannelCache", "Messages/resetCurrentPublicChannelCache", + "Identity/saveUserCsr", "Files/updateMessageMedia", "Messages/addMessages", "Messages/addMessageVerificationStatus", diff --git a/packages/desktop/src/rtl-tests/community.create.test.tsx b/packages/desktop/src/rtl-tests/community.create.test.tsx index 446409f0e2..746c38f217 100644 --- a/packages/desktop/src/rtl-tests/community.create.test.tsx +++ b/packages/desktop/src/rtl-tests/community.create.test.tsx @@ -163,7 +163,7 @@ describe('User', () => { "Identity/registerUsername", "Network/setLoadingPanelType", "Modals/openModal", - "Identity/registerCertificate", + "Identity/addCsr", "Communities/createCommunity", "Files/checkForMissingFiles", "Network/addInitializedCommunity", diff --git a/packages/desktop/src/rtl-tests/community.join.test.tsx b/packages/desktop/src/rtl-tests/community.join.test.tsx index 0d6f4c1af9..aab78bcf86 100644 --- a/packages/desktop/src/rtl-tests/community.join.test.tsx +++ b/packages/desktop/src/rtl-tests/community.join.test.tsx @@ -179,7 +179,7 @@ describe('User', () => { "Identity/registerUsername", "Network/setLoadingPanelType", "Modals/openModal", - "Identity/registerCertificate", + "Identity/addCsr", "Communities/launchCommunity", "Files/checkForMissingFiles", "Network/addInitializedCommunity", @@ -195,6 +195,7 @@ describe('User', () => { "Messages/lazyLoading", "Messages/resetCurrentPublicChannelCache", "Messages/resetCurrentPublicChannelCache", + "Identity/saveUserCsr", "Messages/addMessagesSendingStatus", "Messages/addMessageVerificationStatus", "Messages/addMessages", diff --git a/packages/identity/src/extractPubKey.ts b/packages/identity/src/extractPubKey.ts index d4bb0230c0..6f66224818 100644 --- a/packages/identity/src/extractPubKey.ts +++ b/packages/identity/src/extractPubKey.ts @@ -8,6 +8,7 @@ import config from './config' import { getAlgorithmParameters, Certificate, CertificationRequest, getCrypto } from 'pkijs' import { NoCryptoEngineError } from '@quiet/types' +// FIXME: This is a duplicate of loadCertificate export const parseCertificate = (pem: string): Certificate => { let certificateBuffer = new ArrayBuffer(0) certificateBuffer = stringToArrayBuffer(fromBase64(pem)) diff --git a/packages/integration-tests/src/integrationTests/appActions.ts b/packages/integration-tests/src/integrationTests/appActions.ts index dd789afdb7..1404683cb1 100644 --- a/packages/integration-tests/src/integrationTests/appActions.ts +++ b/packages/integration-tests/src/integrationTests/appActions.ts @@ -162,7 +162,8 @@ export async function sendCsr(store: Store) { userCsr, } - store.dispatch(identity.actions.registerCertificate(csr)) + store.dispatch(identity.actions.addCsr(csr)) + store.dispatch(identity.actions.saveUserCsr()) } export async function joinCommunity(payload: JoinCommunity) { diff --git a/packages/mobile/src/screens/Channel/Channel.screen.tsx b/packages/mobile/src/screens/Channel/Channel.screen.tsx index c399e8d8d1..010e8e0289 100644 --- a/packages/mobile/src/screens/Channel/Channel.screen.tsx +++ b/packages/mobile/src/screens/Channel/Channel.screen.tsx @@ -110,7 +110,7 @@ export const ChannelScreen: FC = () => { return updatedExistingFiles }) - //User Label + // User Label const duplicatedUsernameHandleBack = useCallback(() => { dispatch( diff --git a/packages/mobile/src/store/init/startConnection/startConnection.saga.ts b/packages/mobile/src/store/init/startConnection/startConnection.saga.ts index 5a4d63ce1b..e9f9a220ae 100644 --- a/packages/mobile/src/store/init/startConnection/startConnection.saga.ts +++ b/packages/mobile/src/store/init/startConnection/startConnection.saga.ts @@ -1,11 +1,12 @@ import { io } from 'socket.io-client' -import { select, put, call, cancel, fork, takeEvery, FixedTask } from 'typed-redux-saga' +import { select, put, call, cancel, fork, takeEvery, FixedTask, delay, apply } from 'typed-redux-saga' import { PayloadAction } from '@reduxjs/toolkit' import { socket as stateManager, Socket } from '@quiet/state-manager' import { encodeSecret } from '@quiet/common' import { initSelectors } from '../init.selectors' import { initActions, WebsocketConnectionPayload } from '../init.slice' import { eventChannel } from 'redux-saga' +import { SocketActionTypes } from '@quiet/types' export function* startConnectionSaga( action: PayloadAction['payload']> @@ -25,6 +26,7 @@ export function* startConnectionSaga( return } + console.log('Connecting to backend') const token = encodeSecret(socketIOSecret) const socket = yield* call(io, `http://127.0.0.1:${_dataPort}`, { withCredentials: true, @@ -42,6 +44,9 @@ function* setConnectedSaga(socket: Socket): Generator { console.log('WEBSOCKET', 'Forking state-manager sagas', task) // Handle suspending current connection yield* takeEvery(initActions.suspendWebsocketConnection, cancelRootTaskSaga, task) + console.log('Frontend is ready. Starting backend...') + // @ts-ignore - Why is this broken? + yield* apply(socket, socket.emit, [SocketActionTypes.START]) } function* handleSocketLifecycleActions(socket: Socket, socketIOData: WebsocketConnectionPayload): Generator { diff --git a/packages/state-manager/src/sagas/communities/createCommunity/createCommunity.saga.ts b/packages/state-manager/src/sagas/communities/createCommunity/createCommunity.saga.ts index ebed5ee240..fcbb78c499 100644 --- a/packages/state-manager/src/sagas/communities/createCommunity/createCommunity.saga.ts +++ b/packages/state-manager/src/sagas/communities/createCommunity/createCommunity.saga.ts @@ -21,7 +21,10 @@ export function* createCommunitySaga( const community = yield* select(communitiesSelectors.selectById(communityId)) const identity = yield* select(identitySelectors.selectById(communityId)) - if (!identity) return + if (!identity) { + console.error('Failed to create community - identity missing') + return + } const payload: InitCommunityPayload = { id: communityId, @@ -41,7 +44,10 @@ export function* createCommunitySaga( applyEmitParams(SocketActionTypes.CREATE_COMMUNITY, payload) ) - if (!createdCommunity || !createdCommunity.ownerCertificate) return + if (!createdCommunity || !createdCommunity.ownerCertificate) { + console.error('Failed to create community - invalid response from backend') + return + } yield* put(communitiesActions.updateCommunityData(createdCommunity)) diff --git a/packages/state-manager/src/sagas/communities/launchCommunity/launchCommunity.saga.ts b/packages/state-manager/src/sagas/communities/launchCommunity/launchCommunity.saga.ts index 278a7d22e2..0996a55318 100644 --- a/packages/state-manager/src/sagas/communities/launchCommunity/launchCommunity.saga.ts +++ b/packages/state-manager/src/sagas/communities/launchCommunity/launchCommunity.saga.ts @@ -1,6 +1,7 @@ import { apply, select, put, call } from 'typed-redux-saga' import { type PayloadAction } from '@reduxjs/toolkit' import { applyEmitParams, type Socket } from '../../../types' +import { identityActions } from '../../identity/identity.slice' import { identitySelectors } from '../../identity/identity.selectors' import { communitiesSelectors } from '../communities.selectors' import { communitiesActions } from '../communities.slice' @@ -29,6 +30,8 @@ export function* launchCommunitySaga( socket: Socket, action: PayloadAction['payload']> ): Generator { + console.log('Launching community') + const communityId = action.payload if (!communityId) { @@ -63,4 +66,6 @@ export function* launchCommunitySaga( } yield* apply(socket, socket.emitWithAck, applyEmitParams(SocketActionTypes.LAUNCH_COMMUNITY, payload)) + + yield* put(identityActions.saveUserCsr()) } diff --git a/packages/state-manager/src/sagas/identity/checkLocalCsr/checkLocalCsr.saga.test.ts b/packages/state-manager/src/sagas/identity/checkLocalCsr/checkLocalCsr.saga.test.ts deleted file mode 100644 index 7804f2fc5c..0000000000 --- a/packages/state-manager/src/sagas/identity/checkLocalCsr/checkLocalCsr.saga.test.ts +++ /dev/null @@ -1,103 +0,0 @@ -import { createUserCsr, getPubKey, loadPrivateKey, pubKeyFromCsr, setupCrypto } from '@quiet/identity' -import { FactoryGirl } from 'factory-girl' -import { getFactory } from '../../../utils/tests/factories' -import { prepareStore, reducers } from '../../../utils/tests/prepareStore' -import { Store, combineReducers } from 'redux' -import { communitiesActions } from '../../communities/communities.slice' -import { identityActions } from '../identity.slice' -import { checkLocalCsrSaga } from './checkLocalCsr.saga' -import { CreateUserCsrPayload, SendCsrsResponse } from '@quiet/types' -import { expectSaga } from 'redux-saga-test-plan' -import { usersActions } from '../../users/users.slice' - -describe('checkLocalCsr', () => { - let store: Store - let factory: FactoryGirl - - beforeEach(async () => { - setupCrypto() - store = prepareStore().store - factory = await getFactory(store) - }) - - test('saves user csr if absent from the database', async () => { - const community = - await factory.create['payload']>('Community') - - const identity = await factory.create['payload']>('Identity', { - id: community.id, - nickname: 'john', - }) - - const payload: SendCsrsResponse = { - csrs: [], - } - - const reducer = combineReducers(reducers) - await expectSaga(checkLocalCsrSaga, usersActions.storeCsrs(payload)) - .withReducer(reducer) - .withState(store.getState()) - .put(identityActions.saveUserCsr()) - .run() - }) - - test('saves user csr if local and stored one differs', async () => { - const community = - await factory.create['payload']>('Community') - - const identity = await factory.create['payload']>('Identity', { - id: community.id, - nickname: 'john', - }) - - const _pubKey = pubKeyFromCsr(identity.userCsr!.userCsr) - - const privateKey = await loadPrivateKey(identity.userCsr!.userKey, 'ECDSA') - const publicKey = await getPubKey(_pubKey) - - const existingKeyPair: CryptoKeyPair = { privateKey, publicKey } - - const createUserCsrPayload: CreateUserCsrPayload = { - nickname: 'alice', - commonName: identity.hiddenService.onionAddress, - peerId: identity.peerId.id, - signAlg: 'ECDSA', - hashAlg: 'sha-256', - existingKeyPair, - } - - const csr = await createUserCsr(createUserCsrPayload) - - const payload: SendCsrsResponse = { - csrs: [csr.userCsr], - } - - const reducer = combineReducers(reducers) - await expectSaga(checkLocalCsrSaga, usersActions.storeCsrs(payload)) - .withReducer(reducer) - .withState(store.getState()) - .put(identityActions.saveUserCsr()) - .run() - }) - - test('skips if stored csr equals local one', async () => { - const community = - await factory.create['payload']>('Community') - - const identity = await factory.create['payload']>('Identity', { - id: community.id, - nickname: 'john', - }) - - const payload: SendCsrsResponse = { - csrs: [identity.userCsr!.userCsr], - } - - const reducer = combineReducers(reducers) - await expectSaga(checkLocalCsrSaga, usersActions.storeCsrs(payload)) - .withReducer(reducer) - .withState(store.getState()) - .not.put(identityActions.saveUserCsr()) - .run() - }) -}) diff --git a/packages/state-manager/src/sagas/identity/checkLocalCsr/checkLocalCsr.saga.ts b/packages/state-manager/src/sagas/identity/checkLocalCsr/checkLocalCsr.saga.ts deleted file mode 100644 index 2e57894e01..0000000000 --- a/packages/state-manager/src/sagas/identity/checkLocalCsr/checkLocalCsr.saga.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { select, call, put } from 'typed-redux-saga' -import { PayloadAction } from '@reduxjs/toolkit' -import { identityActions } from '../identity.slice' -import { identitySelectors } from '../identity.selectors' -import { CertFieldsTypes, getReqFieldValue, loadCSR, pubKeyFromCsr } from '@quiet/identity' - -export function* checkLocalCsrSaga( - action: PayloadAction['payload']> -): Generator { - console.log('Checking local CSR', action.payload.csrs) - - const { csrs } = action.payload - - const identity = yield* select(identitySelectors.currentIdentity) - - if (!identity) { - console.error('Could not check local csr, no identity.') - return - } - - if (!identity.userCsr) { - console.warn("Identity doesn't have userCsr.") - return - } - - const pubKey = yield* call(pubKeyFromCsr, identity.userCsr.userCsr) - - const storedCsr = csrs.find(csr => csr === identity.userCsr?.userCsr) - - if (storedCsr) { - console.log('Stored CSR with the same public key found, checking for username integirty.', pubKey) - - const parsedCsr = yield* call(loadCSR, storedCsr) - const nickname = yield* call(getReqFieldValue, parsedCsr, CertFieldsTypes.nickName) - - if (nickname == identity.nickname) { - console.log('Stored CSR is equal to the local one, skipping.') - return - } - } - - console.log('Stored CSR differs or missing, saving local one.') - - yield* put(identityActions.saveUserCsr()) -} diff --git a/packages/state-manager/src/sagas/identity/identity.master.saga.ts b/packages/state-manager/src/sagas/identity/identity.master.saga.ts index 471398a152..cf3c116db7 100644 --- a/packages/state-manager/src/sagas/identity/identity.master.saga.ts +++ b/packages/state-manager/src/sagas/identity/identity.master.saga.ts @@ -1,20 +1,16 @@ import { type Socket } from '../../types' import { all, takeEvery } from 'typed-redux-saga' import { identityActions } from './identity.slice' -import { registerCertificateSaga } from './registerCertificate/registerCertificate.saga' import { registerUsernameSaga } from './registerUsername/registerUsername.saga' import { verifyJoinTimestampSaga } from './verifyJoinTimestamp/verifyJoinTimestamp.saga' import { saveUserCsrSaga } from './saveUserCsr/saveUserCsr.saga' import { usersActions } from '../users/users.slice' import { updateCertificateSaga } from './updateCertificate/updateCertificate.saga' -import { checkLocalCsrSaga } from './checkLocalCsr/checkLocalCsr.saga' export function* identityMasterSaga(socket: Socket): Generator { yield all([ takeEvery(identityActions.registerUsername.type, registerUsernameSaga, socket), - takeEvery(identityActions.registerCertificate.type, registerCertificateSaga, socket), takeEvery(identityActions.verifyJoinTimestamp.type, verifyJoinTimestampSaga), - takeEvery(identityActions.checkLocalCsr.type, checkLocalCsrSaga), takeEvery(identityActions.saveUserCsr.type, saveUserCsrSaga, socket), takeEvery(usersActions.responseSendCertificates.type, updateCertificateSaga), ]) diff --git a/packages/state-manager/src/sagas/identity/identity.slice.ts b/packages/state-manager/src/sagas/identity/identity.slice.ts index ec7eb25502..6915b1edb4 100644 --- a/packages/state-manager/src/sagas/identity/identity.slice.ts +++ b/packages/state-manager/src/sagas/identity/identity.slice.ts @@ -29,9 +29,8 @@ export const identitySlice = createSlice({ changes: action.payload, }) }, - createUserCsr: (state, _action: PayloadAction) => state, registerUsername: (state, _action: PayloadAction) => state, - registerCertificate: (state, action: PayloadAction) => { + addCsr: (state, action: PayloadAction) => { identityAdapter.updateOne(state.identities, { id: action.payload.communityId, changes: { @@ -49,7 +48,6 @@ export const identitySlice = createSlice({ }, }) }, - checkLocalCsr: (state, _action: PayloadAction) => state, saveUserCsr: state => state, verifyJoinTimestamp: state => state, updateJoinTimestamp: (state, action: PayloadAction) => { @@ -60,7 +58,6 @@ export const identitySlice = createSlice({ }, }) }, - throwIdentityError: (state, _action: PayloadAction) => state, }, }) diff --git a/packages/state-manager/src/sagas/identity/registerCertificate/registerCertificate.saga.test.ts b/packages/state-manager/src/sagas/identity/registerCertificate/registerCertificate.saga.test.ts deleted file mode 100644 index 43da4a2ade..0000000000 --- a/packages/state-manager/src/sagas/identity/registerCertificate/registerCertificate.saga.test.ts +++ /dev/null @@ -1,146 +0,0 @@ -import { expectSaga } from 'redux-saga-test-plan' -import { type Socket } from '../../../types' -import { setupCrypto } from '@quiet/identity' -import { prepareStore } from '../../../utils/tests/prepareStore' -import { getFactory } from '../../../utils/tests/factories' -import { combineReducers } from '@reduxjs/toolkit' -import { reducers } from '../../reducers' -import { communitiesActions } from '../../communities/communities.slice' -import { identityActions } from '../identity.slice' -import { registerCertificateSaga } from './registerCertificate.saga' -import { type CertData, type RegisterCertificatePayload, SocketActionTypes, type UserCsr } from '@quiet/types' - -describe('registerCertificateSaga', () => { - it('create community when user is community owner', async () => { - setupCrypto() - const socket = { emit: jest.fn(), on: jest.fn() } as unknown as Socket - const store = prepareStore().store - - const factory = await getFactory(store) - - const community = - await factory.create['payload']>('Community') - - const identity = await factory.create['payload']>('Identity', { - id: community.id, - }) - expect(identity.userCsr).not.toBeNull() - const registerCertificatePayload: RegisterCertificatePayload = { - communityId: community.id, - nickname: identity.nickname, - // @ts-expect-error - userCsr: identity.userCsr, - } - const reducer = combineReducers(reducers) - await expectSaga(registerCertificateSaga, socket, identityActions.registerCertificate(registerCertificatePayload)) - .withReducer(reducer) - .withState(store.getState()) - .put(communitiesActions.createCommunity(community.id)) - .not.apply(socket, socket.emit, [SocketActionTypes.REGISTER_USER_CERTIFICATE]) - .run() - }) - - it('launch community when user is not community owner', async () => { - setupCrypto() - const socket = { emit: jest.fn(), on: jest.fn() } as unknown as Socket - - const store = prepareStore().store - - const factory = await getFactory(store) - - const community = await factory.create['payload']>( - 'Community', - { - id: '1', - name: 'rockets', - CA: null, - rootCa: 'rootCa', - peerList: [], - onionAddress: '', - } - ) - - const userCsr: UserCsr = { - userCsr: 'userCsr', - userKey: 'userKey', - pkcs10: jest.fn() as unknown as CertData, - } - - const identity = ( - await factory.build('Identity', { - id: community.id, - }) - ).payload - - identity.userCsr = userCsr - - store.dispatch(identityActions.addNewIdentity(identity)) - - const registerCertificatePayload: RegisterCertificatePayload = { - communityId: community.id, - nickname: identity.nickname, - userCsr: identity.userCsr, - } - - const reducer = combineReducers(reducers) - await expectSaga(registerCertificateSaga, socket, identityActions.registerCertificate(registerCertificatePayload)) - .withReducer(reducer) - .withState(store.getState()) - .not.put(communitiesActions.createCommunity(community.id)) - .put(communitiesActions.launchCommunity(community.id)) - .run() - }) - - it('launch community when user is not community owner and he used username which was taken', async () => { - setupCrypto() - const socket = { emit: jest.fn(), on: jest.fn() } as unknown as Socket - - const store = prepareStore().store - - const factory = await getFactory(store) - - const community = await factory.create['payload']>( - 'Community', - { - id: '1', - name: 'rockets', - CA: null, - rootCa: 'rootCa', - peerList: [], - onionAddress: '', - } - ) - - const userCsr: UserCsr = { - userCsr: 'userCsr', - userKey: 'userKey', - pkcs10: jest.fn() as unknown as CertData, - } - - const identity = ( - await factory.build('Identity', { - id: community.id, - }) - ).payload - - identity.userCsr = userCsr - - store.dispatch(identityActions.addNewIdentity(identity)) - - const registerCertificatePayload: RegisterCertificatePayload = { - communityId: community.id, - nickname: identity.nickname, - userCsr: identity.userCsr, - isUsernameTaken: true, - } - - const reducer = combineReducers(reducers) - await expectSaga(registerCertificateSaga, socket, identityActions.registerCertificate(registerCertificatePayload)) - .withReducer(reducer) - .withState(store.getState()) - .not.put(communitiesActions.createCommunity(community.id)) - .not.put(communitiesActions.launchCommunity(community.id)) - .put(identityActions.saveUserCsr()) - .run() - }) -}) diff --git a/packages/state-manager/src/sagas/identity/registerCertificate/registerCertificate.saga.ts b/packages/state-manager/src/sagas/identity/registerCertificate/registerCertificate.saga.ts deleted file mode 100644 index 31a46349ee..0000000000 --- a/packages/state-manager/src/sagas/identity/registerCertificate/registerCertificate.saga.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { applyEmitParams, type Socket } from '../../../types' -import { type PayloadAction } from '@reduxjs/toolkit' -import { apply, select, put } from 'typed-redux-saga' -import { communitiesSelectors } from '../../communities/communities.selectors' -import { identityActions } from '../identity.slice' -import { - type RegisterOwnerCertificatePayload, - type RegisterUserCertificatePayload, - SocketActionTypes, -} from '@quiet/types' -import { communitiesActions } from '../../communities/communities.slice' - -export function* registerCertificateSaga( - socket: Socket, - action: PayloadAction['payload']> -): Generator { - const currentCommunity = yield* select(communitiesSelectors.currentCommunity) - const isUsernameTaken = action.payload.isUsernameTaken - - if (!currentCommunity) { - console.error('Could not register certificate, no current community') - return - } - - if (currentCommunity.CA?.rootCertString) { - yield* put(communitiesActions.createCommunity(action.payload.communityId)) - } else { - if (!isUsernameTaken) { - yield* put(communitiesActions.launchCommunity(action.payload.communityId)) - } else { - yield* put(identityActions.saveUserCsr()) - } - } -} diff --git a/packages/state-manager/src/sagas/identity/registerUsername/registerUsername.saga.test.ts b/packages/state-manager/src/sagas/identity/registerUsername/registerUsername.saga.test.ts index 5acf2b3e7b..327b7cb698 100644 --- a/packages/state-manager/src/sagas/identity/registerUsername/registerUsername.saga.test.ts +++ b/packages/state-manager/src/sagas/identity/registerUsername/registerUsername.saga.test.ts @@ -59,7 +59,7 @@ describe('registerUsernameSaga', () => { .provide([[call.fn(createUserCsr), userCsr]]) .call(createUserCsr, createUserCsrPayload) .put( - identityActions.registerCertificate({ + identityActions.addCsr({ communityId: community.id, nickname: 'nickname', userCsr, @@ -131,7 +131,7 @@ describe('registerUsernameSaga', () => { .call(getPubKey, pubKey) .call(createUserCsr, createUserCsrPayload) .put( - identityActions.registerCertificate({ + identityActions.addCsr({ communityId: community.id, nickname: newNickname, userCsr, @@ -187,7 +187,7 @@ describe('registerUsernameSaga', () => { ]) .dispatch(identityActions.addNewIdentity(identity)) .put( - identityActions.registerCertificate({ + identityActions.addCsr({ communityId: community.id, nickname: identity.nickname, userCsr, @@ -272,7 +272,7 @@ describe('registerUsernameSaga', () => { .dispatch(identityActions.addNewIdentity(identity)) .call(createUserCsr, createUserCsrPayload) .put( - identityActions.registerCertificate({ + identityActions.addCsr({ communityId: community.id, nickname: 'nickname', userCsr, diff --git a/packages/state-manager/src/sagas/identity/registerUsername/registerUsername.saga.ts b/packages/state-manager/src/sagas/identity/registerUsername/registerUsername.saga.ts index ae91990777..e6714f430c 100644 --- a/packages/state-manager/src/sagas/identity/registerUsername/registerUsername.saga.ts +++ b/packages/state-manager/src/sagas/identity/registerUsername/registerUsername.saga.ts @@ -13,35 +13,35 @@ export function* registerUsernameSaga( socket: Socket, action: PayloadAction['payload']> ): Generator { + console.log('Registering username') + // Nickname can differ between saga calls const { nickname, isUsernameTaken = false } = action.payload let community = yield* select(communitiesSelectors.currentCommunity) - if (!community) { + console.warn('Community missing, waiting...') yield* take(communitiesActions.addNewCommunity) } - community = yield* select(communitiesSelectors.currentCommunity) - if (!community) { console.error('Could not register username, no community data') return } + console.log('Found community') let identity = yield* select(identitySelectors.currentIdentity) - if (!identity) { + console.warn('Identity missing, waiting...') yield* take(identityActions.addNewIdentity) } - identity = yield* select(identitySelectors.currentIdentity) - if (!identity) { console.error('Could not register username, no identity') return } + console.log('Found identity') let userCsr = identity.userCsr @@ -87,12 +87,24 @@ export function* registerUsernameSaga( } } + // TODO: Can rename this type const payload: RegisterCertificatePayload = { communityId: community.id, nickname, userCsr, + // TODO: Remove isUsernameTaken, } - yield* put(identityActions.registerCertificate(payload)) + yield* put(identityActions.addCsr(payload)) + + if (community.CA?.rootCertString) { + yield* put(communitiesActions.createCommunity(community.id)) + } else { + if (!isUsernameTaken) { + yield* put(communitiesActions.launchCommunity(community.id)) + } else { + yield* put(identityActions.saveUserCsr()) + } + } } diff --git a/packages/state-manager/src/sagas/identity/saveUserCsr/saveUserCsr.saga.ts b/packages/state-manager/src/sagas/identity/saveUserCsr/saveUserCsr.saga.ts index 8a595c0fa1..05c371bef0 100644 --- a/packages/state-manager/src/sagas/identity/saveUserCsr/saveUserCsr.saga.ts +++ b/packages/state-manager/src/sagas/identity/saveUserCsr/saveUserCsr.saga.ts @@ -4,6 +4,8 @@ import { apply, select } from 'typed-redux-saga' import { identitySelectors } from '../identity.selectors' export function* saveUserCsrSaga(socket: Socket): Generator { + console.log('Saving user CSR') + const identity = yield* select(identitySelectors.currentIdentity) if (!identity?.userCsr) { console.error('Cannot save user csr to backend, no userCsr') diff --git a/packages/state-manager/src/sagas/messages/messages.slice.ts b/packages/state-manager/src/sagas/messages/messages.slice.ts index c279efc8b2..ab7bfc098a 100644 --- a/packages/state-manager/src/sagas/messages/messages.slice.ts +++ b/packages/state-manager/src/sagas/messages/messages.slice.ts @@ -78,8 +78,14 @@ export const messagesSlice = createSlice({ addMessages: (state, action: PayloadAction) => { const { messages } = action.payload for (const message of messages) { - if (!instanceOfChannelMessage(message)) return - if (!state.publicChannelsMessagesBase.entities[message.channelId]) return + if (!instanceOfChannelMessage(message)) { + console.error('Failed to add message, object not instance of message') + return + } + if (!state.publicChannelsMessagesBase.entities[message.channelId]) { + console.error('Failed to add message, could not find channel', message.channelId) + return + } let toAdd = message @@ -96,8 +102,11 @@ export const messagesSlice = createSlice({ } const messagesBase = state.publicChannelsMessagesBase.entities[message.channelId] - if (!messagesBase) return + if (!messagesBase) { + throw new Error('Failed to add message, channel went missing') + } + console.log('Upserting message to Redux store') channelMessagesAdapter.upsertOne(messagesBase.messages, toAdd) } }, diff --git a/packages/state-manager/src/sagas/messages/sendMessage/sendMessage.saga.ts b/packages/state-manager/src/sagas/messages/sendMessage/sendMessage.saga.ts index 1297c1ea5b..fee94b4d50 100644 --- a/packages/state-manager/src/sagas/messages/sendMessage/sendMessage.saga.ts +++ b/packages/state-manager/src/sagas/messages/sendMessage/sendMessage.saga.ts @@ -1,10 +1,11 @@ import { type Socket, applyEmitParams } from '../../../types' import { type PayloadAction } from '@reduxjs/toolkit' import { sign, loadPrivateKey, pubKeyFromCsr } from '@quiet/identity' -import { call, select, apply, put, delay } from 'typed-redux-saga' +import { call, select, apply, put, delay, take } from 'typed-redux-saga' import { arrayBufferToString } from 'pvutils' import { config } from '../../users/const/certFieldTypes' import { identitySelectors } from '../../identity/identity.selectors' +import { publicChannelsActions } from '../../publicChannels/publicChannels.slice' import { publicChannelsSelectors } from '../../publicChannels/publicChannels.selectors' import { messagesActions } from '../messages.slice' import { generateMessageId, getCurrentTime } from '../utils/message.utils' @@ -14,28 +15,30 @@ export function* sendMessageSaga( socket: Socket, action: PayloadAction['payload']> ): Generator { - const identity = yield* select(identitySelectors.currentIdentity) - if (!identity?.userCsr) return - - const pubKey = yield* call(pubKeyFromCsr, identity.userCsr.userCsr) - const keyObject = yield* call(loadPrivateKey, identity.userCsr.userKey, config.signAlg) - const signatureArrayBuffer = yield* call(sign, action.payload.message, keyObject) - const signature = yield* call(arrayBufferToString, signatureArrayBuffer) - - const currentChannelId = yield* select(publicChannelsSelectors.currentChannelId) - - const createdAt = yield* call(getCurrentTime) - const generatedMessageId = yield* call(generateMessageId) - const id = action.payload.id || generatedMessageId + const identity = yield* select(identitySelectors.currentIdentity) + if (!identity?.userCsr) { + console.error(`Failed to send message ${id} - user CSR is missing`) + return + } + + const currentChannelId = yield* select(publicChannelsSelectors.currentChannelId) const channelId = action.payload.channelId || currentChannelId if (!channelId) { - console.error(`Could not send message with id ${id}, no channel id`) + console.error(`Failed to send message ${id} - channel ID is missing`) return } + console.log(`Sending message ${id} to channel ${channelId}`) + + const pubKey = yield* call(pubKeyFromCsr, identity.userCsr.userCsr) + const keyObject = yield* call(loadPrivateKey, identity.userCsr.userKey, config.signAlg) + const signatureArrayBuffer = yield* call(sign, action.payload.message, keyObject) + const signature = yield* call(arrayBufferToString, signatureArrayBuffer) + const createdAt = yield* call(getCurrentTime) + const message: ChannelMessage = { id, type: action.payload.type || MessageType.Basic, @@ -48,6 +51,7 @@ export function* sendMessageSaga( } // Grey out message until saved in db + console.log('Adding pending message status') yield* put( messagesActions.addMessagesSendingStatus({ message: message, @@ -65,6 +69,7 @@ export function* sendMessageSaga( ) // Display sent message immediately, to improve user experience + console.log('Adding message to Redux store') yield* put( messagesActions.addMessages({ messages: [message], @@ -73,7 +78,10 @@ export function* sendMessageSaga( ) const isUploadingFileMessage = action.payload.media?.cid?.includes('uploading') - if (isUploadingFileMessage) return // Do not broadcast message until file is uploaded + if (isUploadingFileMessage) { + console.log(`Failed to send message ${id} - file upload is in progress`) + return // Do not broadcast message until file is uploaded + } // Wait until we have subscribed to the channel // @@ -82,11 +90,13 @@ export function* sendMessageSaga( // (in a durable way). while (true) { const subscribedChannels = yield* select(publicChannelsSelectors.subscribedChannels) + console.log('Subscribed channels', subscribedChannels) if (subscribedChannels.includes(channelId)) { + console.log(`Channel ${channelId} subscribed`) break } - console.error('Failed to send message, channel not subscribed. Retrying...') - yield* delay(500) + console.error(`Failed to send message ${id} - channel not subscribed. Waiting...`) + yield* take(publicChannelsActions.setChannelSubscribed) } yield* apply( diff --git a/packages/state-manager/src/sagas/publicChannels/publicChannels.selectors.ts b/packages/state-manager/src/sagas/publicChannels/publicChannels.selectors.ts index 1481087c42..fb5de0ab36 100644 --- a/packages/state-manager/src/sagas/publicChannels/publicChannels.selectors.ts +++ b/packages/state-manager/src/sagas/publicChannels/publicChannels.selectors.ts @@ -178,6 +178,8 @@ export const displayableCurrentChannelMessages = createSelector( if (user) { // @ts-ignore result.push(displayableMessage(message, user, userProfiles[message.pubKey])) + } else { + console.warn('Received a message from a user that does not exist', message.id, message.pubKey, users) } return result }, []) diff --git a/packages/state-manager/src/sagas/socket/startConnection/startConnection.saga.ts b/packages/state-manager/src/sagas/socket/startConnection/startConnection.saga.ts index 9c908f13ee..a64a65224c 100644 --- a/packages/state-manager/src/sagas/socket/startConnection/startConnection.saga.ts +++ b/packages/state-manager/src/sagas/socket/startConnection/startConnection.saga.ts @@ -64,8 +64,6 @@ export function subscribe(socket: Socket) { | ReturnType | ReturnType | ReturnType - | ReturnType - | ReturnType | ReturnType | ReturnType | ReturnType @@ -160,7 +158,6 @@ export function subscribe(socket: Socket) { // Certificates socket.on(SocketActionTypes.CSRS_STORED, (payload: SendCsrsResponse) => { log(`${SocketActionTypes.CSRS_STORED}`) - emit(identityActions.checkLocalCsr(payload)) emit(usersActions.storeCsrs(payload)) }) socket.on(SocketActionTypes.CERTIFICATES_STORED, (payload: SendCertificatesResponse) => { diff --git a/packages/state-manager/src/sagas/users/users.selectors.ts b/packages/state-manager/src/sagas/users/users.selectors.ts index 2f21e34b08..4902482597 100644 --- a/packages/state-manager/src/sagas/users/users.selectors.ts +++ b/packages/state-manager/src/sagas/users/users.selectors.ts @@ -77,9 +77,9 @@ export const registeredUsernames = createSelector( mapping => new Set(Object.values(mapping).map(u => u.username)) ) +// TODO: We can move most of this to the backend. export const allUsers = createSelector(csrsMapping, certificatesMapping, (csrs, certs) => { const users: Record = {} - const allUsernames: string[] = Object.values(csrs).map(u => u.username) const duplicatedUsernames: string[] = allUsernames.filter((val, index) => allUsernames.indexOf(val) !== index) diff --git a/packages/state-manager/src/types.ts b/packages/state-manager/src/types.ts index 44260530d5..909ae28c6c 100644 --- a/packages/state-manager/src/types.ts +++ b/packages/state-manager/src/types.ts @@ -52,6 +52,7 @@ export interface EmitEvents { [SocketActionTypes.ADD_CSR]: EmitEvent [SocketActionTypes.SET_USER_PROFILE]: EmitEvent [SocketActionTypes.LOAD_MIGRATION_DATA]: EmitEvent> + [SocketActionTypes.START]: () => void } export type Socket = IOSocket diff --git a/packages/types/src/socket.ts b/packages/types/src/socket.ts index 573fb1a313..c74ba5dd14 100644 --- a/packages/types/src/socket.ts +++ b/packages/types/src/socket.ts @@ -87,4 +87,10 @@ export enum SocketActionTypes { MIGRATION_DATA_REQUIRED = 'migrationDataRequired', PUSH_NOTIFICATION = 'pushNotification', ERROR = 'error', + /** + * Start the backend. Currently, the frontend depends on events + * emitted from the backend, so we wait to start the backend until + * the frontend is connected and listening. + */ + START = 'start', } From 2ad27787b40e5d801302b4e4977cd3ca314c7d2e Mon Sep 17 00:00:00 2001 From: Lucas Leblow Date: Tue, 30 Apr 2024 09:01:01 -0700 Subject: [PATCH 20/48] Publish - @quiet/desktop@2.2.0-alpha.3 - @quiet/mobile@2.2.0-alpha.3 --- packages/desktop/CHANGELOG.md | 16 ++++++++++++++++ packages/desktop/package-lock.json | 4 ++-- packages/desktop/package.json | 2 +- packages/mobile/CHANGELOG.md | 18 ++++++++++++++++++ packages/mobile/android/app/build.gradle | 4 ++-- packages/mobile/ios/Quiet/Info.plist | 2 +- packages/mobile/ios/QuietTests/Info.plist | 2 +- packages/mobile/package-lock.json | 4 ++-- packages/mobile/package.json | 2 +- 9 files changed, 44 insertions(+), 10 deletions(-) diff --git a/packages/desktop/CHANGELOG.md b/packages/desktop/CHANGELOG.md index e5ad80e5c2..e8a6e95695 100644 --- a/packages/desktop/CHANGELOG.md +++ b/packages/desktop/CHANGELOG.md @@ -1,3 +1,19 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +# [2.2.0-alpha.3](/compare/@quiet/desktop@2.2.0-alpha.2...@quiet/desktop@2.2.0-alpha.3) (2024-04-30) + + +### Bug Fixes + +* Various fixes related to peers, CSRs and backend startup (#2455) 2af0531, closes #2455 + + + + + [unreleased] # New features: diff --git a/packages/desktop/package-lock.json b/packages/desktop/package-lock.json index 8c5ca79ed2..94aef27a84 100644 --- a/packages/desktop/package-lock.json +++ b/packages/desktop/package-lock.json @@ -1,12 +1,12 @@ { "name": "@quiet/desktop", - "version": "2.2.0-alpha.2", + "version": "2.2.0-alpha.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@quiet/desktop", - "version": "2.2.0-alpha.2", + "version": "2.2.0-alpha.3", "license": "GPL-3.0-or-later", "dependencies": { "@electron/remote": "^2.0.8", diff --git a/packages/desktop/package.json b/packages/desktop/package.json index 303b5cbb7b..31f8fd265d 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -80,7 +80,7 @@ }, "homepage": "https://github.com/TryQuiet", "@comment version": "To build new version for specific platform, just replace platform in version tag to one of following linux, mac, windows", - "version": "2.2.0-alpha.2", + "version": "2.2.0-alpha.3", "description": "Decentralized team chat", "main": "dist/main/main.js", "scripts": { diff --git a/packages/mobile/CHANGELOG.md b/packages/mobile/CHANGELOG.md index e5ad80e5c2..2990f206f7 100644 --- a/packages/mobile/CHANGELOG.md +++ b/packages/mobile/CHANGELOG.md @@ -1,3 +1,21 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +# [2.2.0-alpha.3](/compare/@quiet/mobile@2.2.0-alpha.2...@quiet/mobile@2.2.0-alpha.3) (2024-04-30) + + +### Bug Fixes + +* change ci emulator name configuration (#2463) e2f0204, closes #2463 +* postpone restore connection saga (#2462) 87ed565, closes #2462 +* Various fixes related to peers, CSRs and backend startup (#2455) 2af0531, closes #2455 + + + + + [unreleased] # New features: diff --git a/packages/mobile/android/app/build.gradle b/packages/mobile/android/app/build.gradle index 6449017724..ab1c47b248 100644 --- a/packages/mobile/android/app/build.gradle +++ b/packages/mobile/android/app/build.gradle @@ -192,8 +192,8 @@ android { applicationId "com.quietmobile" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 414 - versionName "2.2.0-alpha.2" + versionCode 415 + versionName "2.2.0-alpha.3" resValue "string", "build_config_package", "com.quietmobile" testBuildType System.getProperty('testBuildType', 'debug') testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' diff --git a/packages/mobile/ios/Quiet/Info.plist b/packages/mobile/ios/Quiet/Info.plist index 51846dee44..b628587bef 100644 --- a/packages/mobile/ios/Quiet/Info.plist +++ b/packages/mobile/ios/Quiet/Info.plist @@ -34,7 +34,7 @@ CFBundleVersion - 371 + 372 ITSAppUsesNonExemptEncryption LSRequiresIPhoneOS diff --git a/packages/mobile/ios/QuietTests/Info.plist b/packages/mobile/ios/QuietTests/Info.plist index d450792c6a..9932ae800d 100644 --- a/packages/mobile/ios/QuietTests/Info.plist +++ b/packages/mobile/ios/QuietTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 371 + 372 diff --git a/packages/mobile/package-lock.json b/packages/mobile/package-lock.json index 706228ed3c..440ba5b932 100644 --- a/packages/mobile/package-lock.json +++ b/packages/mobile/package-lock.json @@ -1,12 +1,12 @@ { "name": "@quiet/mobile", - "version": "2.2.0-alpha.2", + "version": "2.2.0-alpha.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@quiet/mobile", - "version": "2.2.0-alpha.2", + "version": "2.2.0-alpha.3", "dependencies": { "@peculiar/webcrypto": "^1.4.3", "@react-native-clipboard/clipboard": "^1.13.2", diff --git a/packages/mobile/package.json b/packages/mobile/package.json index 2d8395a009..c64bb7a325 100644 --- a/packages/mobile/package.json +++ b/packages/mobile/package.json @@ -1,6 +1,6 @@ { "name": "@quiet/mobile", - "version": "2.2.0-alpha.2", + "version": "2.2.0-alpha.3", "scripts": { "build": "tsc -p tsconfig.build.json --noEmit", "storybook-android": "ENVFILE=.env.storybook react-native run-android --mode=storybookDebug --appIdSuffix=storybook.debug", From 5e5e2fc3ee4629daed053f726bed123aef625af3 Mon Sep 17 00:00:00 2001 From: Lucas Leblow Date: Tue, 30 Apr 2024 09:01:04 -0700 Subject: [PATCH 21/48] Update packages CHANGELOG.md --- packages/desktop/CHANGELOG.md | 18 ++---------------- packages/mobile/CHANGELOG.md | 20 ++------------------ 2 files changed, 4 insertions(+), 34 deletions(-) diff --git a/packages/desktop/CHANGELOG.md b/packages/desktop/CHANGELOG.md index e8a6e95695..dcc0e8492c 100644 --- a/packages/desktop/CHANGELOG.md +++ b/packages/desktop/CHANGELOG.md @@ -1,19 +1,3 @@ -# Change Log - -All notable changes to this project will be documented in this file. -See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - -# [2.2.0-alpha.3](/compare/@quiet/desktop@2.2.0-alpha.2...@quiet/desktop@2.2.0-alpha.3) (2024-04-30) - - -### Bug Fixes - -* Various fixes related to peers, CSRs and backend startup (#2455) 2af0531, closes #2455 - - - - - [unreleased] # New features: @@ -22,6 +6,8 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline # Fixes: +* Don't create duplicate CSRs when joining a community under certain circumstances ([#2321](https://github.com/TryQuiet/quiet/issues/2321)) + [2.2.0] # New features: diff --git a/packages/mobile/CHANGELOG.md b/packages/mobile/CHANGELOG.md index 2990f206f7..dcc0e8492c 100644 --- a/packages/mobile/CHANGELOG.md +++ b/packages/mobile/CHANGELOG.md @@ -1,21 +1,3 @@ -# Change Log - -All notable changes to this project will be documented in this file. -See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - -# [2.2.0-alpha.3](/compare/@quiet/mobile@2.2.0-alpha.2...@quiet/mobile@2.2.0-alpha.3) (2024-04-30) - - -### Bug Fixes - -* change ci emulator name configuration (#2463) e2f0204, closes #2463 -* postpone restore connection saga (#2462) 87ed565, closes #2462 -* Various fixes related to peers, CSRs and backend startup (#2455) 2af0531, closes #2455 - - - - - [unreleased] # New features: @@ -24,6 +6,8 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline # Fixes: +* Don't create duplicate CSRs when joining a community under certain circumstances ([#2321](https://github.com/TryQuiet/quiet/issues/2321)) + [2.2.0] # New features: From d3b822b795e0bd2e79ca0104897b63ef93701f9a Mon Sep 17 00:00:00 2001 From: Wiktor Sieprawski Date: Mon, 6 May 2024 12:49:36 +0200 Subject: [PATCH 22/48] fix: start websocket connection on react init (#2481) * fix: start websocket connection on react init * chore: restore self hosted ios e2e tests * fix: prevent stripping hermes framework * Revert "fix: prevent stripping hermes framework" This reverts commit 65dfec8db70de0b0783782cafb25b451f369a109. * chore: skip self-hosted ios e2e tests --- .github/workflows/e2e-ios-self.yml | 51 +++++++++++++++++++ packages/mobile/ios/CommunicationBridge.m | 1 + packages/mobile/ios/CommunicationModule.swift | 7 +++ .../ios/Quiet.xcodeproj/project.pbxproj | 4 ++ packages/mobile/ios/Quiet/AppDelegate.m | 37 ++++---------- packages/mobile/ios/WebsocketSingleton.swift | 9 ++++ 6 files changed, 82 insertions(+), 27 deletions(-) create mode 100644 .github/workflows/e2e-ios-self.yml create mode 100644 packages/mobile/ios/WebsocketSingleton.swift diff --git a/.github/workflows/e2e-ios-self.yml b/.github/workflows/e2e-ios-self.yml new file mode 100644 index 0000000000..7b47db414b --- /dev/null +++ b/.github/workflows/e2e-ios-self.yml @@ -0,0 +1,51 @@ +name: Detox E2E iOS (self-hosted) + +on: + # push: + # paths: + # - packages/mobile/** + # - packages/backend/** + # - packages/state-manager/** + # - .github/workflows/e2e-ios-self.yml + +jobs: + detox-ios-self-hosted: + timeout-minutes: 25 + runs-on: [self-hosted, macOS, ARM64, iOS] + + steps: + - uses: actions/checkout@v4 + with: + lfs: true + + - name: Install dependencies + run: | + npm i + npm run lerna bootstrap --scope @quiet/eslint-config,@quiet/logger,@quiet/common,@quiet/types,@quiet/state-manager,@quiet/backend,@quiet/identity,@quiet/mobile,backend-bundle + + - name: Install pods + run: | + cd packages/mobile/ios + pod install + + - name: Install pm2 + run: npm install pm2@latest -g + + - name: Start metro + run: | + cd packages/mobile + pm2 --name METRO start npm -- start + + - name: Build Detox + run: | + cd packages/mobile + detox build -c ios.sim.debug.ci + + - name: Run basic tests + run: | + cd packages/mobile + detox test starter -c ios.sim.debug.ci + + - name: Stop metro + if: always() + run: pm2 stop METRO diff --git a/packages/mobile/ios/CommunicationBridge.m b/packages/mobile/ios/CommunicationBridge.m index 49714ed292..578fa66723 100644 --- a/packages/mobile/ios/CommunicationBridge.m +++ b/packages/mobile/ios/CommunicationBridge.m @@ -2,4 +2,5 @@ #import "React/RCTEventEmitter.h" @interface RCT_EXTERN_MODULE(CommunicationModule, RCTEventEmitter) +RCT_EXTERN_METHOD(handleIncomingEvents:(NSString *)event payload:(NSString *)payload extra:(NSString *)extra) @end diff --git a/packages/mobile/ios/CommunicationModule.swift b/packages/mobile/ios/CommunicationModule.swift index 75c132e14b..069dfecc93 100644 --- a/packages/mobile/ios/CommunicationModule.swift +++ b/packages/mobile/ios/CommunicationModule.swift @@ -29,6 +29,13 @@ class CommunicationModule: RCTEventEmitter { self.sendEvent(withName: CommunicationModule.APP_RESUME_IDENTIFIER, body: nil) } + @objc + func handleIncomingEvents(_ event: NSString, payload: NSString?, extra: NSString?) { + let socketPort = WebsocketSingleton.sharedInstance.socketPort + let socketIOSecret = WebsocketSingleton.sharedInstance.socketIOSecret + self.sendDataPort(port: socketPort, socketIOSecret: socketIOSecret); + } + override func supportedEvents() -> [String]! { return [CommunicationModule.BACKEND_EVENT_IDENTIFIER, CommunicationModule.NOTIFICATION_EVENT_IDENTIFIER, CommunicationModule.STOP_EVENT_IDENTIFIER, CommunicationModule.APP_PAUSE_IDENTIFIER, CommunicationModule.APP_RESUME_IDENTIFIER] } diff --git a/packages/mobile/ios/Quiet.xcodeproj/project.pbxproj b/packages/mobile/ios/Quiet.xcodeproj/project.pbxproj index 489dc64d1f..1f0cee5e40 100644 --- a/packages/mobile/ios/Quiet.xcodeproj/project.pbxproj +++ b/packages/mobile/ios/Quiet.xcodeproj/project.pbxproj @@ -42,6 +42,7 @@ 7F80A59D9EC1440186E5D5CF /* Rubik-SemiBold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = BF73B04135634980BC656D6C /* Rubik-SemiBold.ttf */; }; 80CCB457674F4979A3C5DB06 /* Rubik-MediumItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = EBC2A7699E0A49059904DD8C /* Rubik-MediumItalic.ttf */; }; 8A009A60D84E4B08AB0E8152 /* Rubik-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = E820B3E5514B49EE8C72DECB /* Rubik-Bold.ttf */; }; + 955DC7582BD930B30014725B /* WebsocketSingleton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 955DC7572BD930B30014725B /* WebsocketSingleton.swift */; }; 9EC9E7C54868433990A479EC /* Rubik-ExtraBoldItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 182B8961416E4D4C8A29793D /* Rubik-ExtraBoldItalic.ttf */; }; 9EFEA2C3C6AE0D6FA6A4C079 /* libPods-Quiet-QuietTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = ED4D442ACC913B8E48E2C569 /* libPods-Quiet-QuietTests.a */; }; A43CD77BAC37717C692E6333 /* libPods-Quiet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 47D3FEF6693D5638AD9D0AFF /* libPods-Quiet.a */; }; @@ -606,6 +607,7 @@ 599527D61D41D1899FC6F87B /* Pods-Quiet-QuietTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Quiet-QuietTests.release.xcconfig"; path = "Target Support Files/Pods-Quiet-QuietTests/Pods-Quiet-QuietTests.release.xcconfig"; sourceTree = ""; }; 706E1601C7B649A8A40A7877 /* Rubik-Light.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Rubik-Light.ttf"; path = "../assets/fonts/Rubik-Light.ttf"; sourceTree = ""; }; 84720E58493F44BE8C4784E3 /* Rubik-BoldItalic.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Rubik-BoldItalic.ttf"; path = "../assets/fonts/Rubik-BoldItalic.ttf"; sourceTree = ""; }; + 955DC7572BD930B30014725B /* WebsocketSingleton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebsocketSingleton.swift; sourceTree = ""; }; BF73B04135634980BC656D6C /* Rubik-SemiBold.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Rubik-SemiBold.ttf"; path = "../assets/fonts/Rubik-SemiBold.ttf"; sourceTree = ""; }; CD39A872C9F80A6BA04B8A4A /* Pods-Quiet.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Quiet.release.xcconfig"; path = "Target Support Files/Pods-Quiet/Pods-Quiet.release.xcconfig"; sourceTree = ""; }; E820B3E5514B49EE8C72DECB /* Rubik-Bold.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Rubik-Bold.ttf"; path = "../assets/fonts/Rubik-Bold.ttf"; sourceTree = ""; }; @@ -679,6 +681,7 @@ 1889CA4D26E763E1004ECFBD /* Extensions.swift */, 1868C43B2930E255001D6D5E /* CommunicationModule.swift */, 1868C43D2930EAEA001D6D5E /* CommunicationBridge.m */, + 955DC7572BD930B30014725B /* WebsocketSingleton.swift */, ); name = Quiet; sourceTree = ""; @@ -5140,6 +5143,7 @@ files = ( 18FD2A40296F009E00A2B8C0 /* main.m in Sources */, 1868BCF1292E9212001D6D5E /* rn-bridge.cpp in Sources */, + 955DC7582BD930B30014725B /* WebsocketSingleton.swift in Sources */, 1889CA4E26E763E1004ECFBD /* Extensions.swift in Sources */, 1898143A2934CF70001F39E7 /* TorHandler.swift in Sources */, 1868BCEE292E9212001D6D5E /* RNNodeJsMobile.m in Sources */, diff --git a/packages/mobile/ios/Quiet/AppDelegate.m b/packages/mobile/ios/Quiet/AppDelegate.m index cb92136270..155174e411 100644 --- a/packages/mobile/ios/Quiet/AppDelegate.m +++ b/packages/mobile/ios/Quiet/AppDelegate.m @@ -45,40 +45,30 @@ - (void) createDataDirectory { self.dataPath = [dataDirectory create]; } -- (void) initWebsocketConnection { - /* - * We have to wait for RCTBridge listeners to be initialized, yet we must be sure to deliver the event containing data port information. - * Delay used below can't cause any race condition as websocket won't connect until data server starts listening anyway. - */ - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{ - NSTimeInterval delayInSeconds = 5; - dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); - dispatch_after(popTime, dispatch_get_main_queue(), ^(void) { - [[self.bridge moduleForName:@"CommunicationModule"] sendDataPortWithPort:self.dataPort socketIOSecret:self.socketIOSecret]; - }); - }); -} - - (void) spinupBackend:(BOOL)init { - // (1/6) Find ports to use in tor and backend configuration + // (1/4) Find ports to use in tor and backend configuration Utils *utils = [Utils new]; if (self.socketIOSecret == nil) { - self.socketIOSecret = [utils generateSecretWithLength:(20)]; + self.socketIOSecret = [utils generateSecretWithLength:(20)]; } - + FindFreePort *findFreePort = [FindFreePort new]; self.dataPort = [findFreePort getFirstStartingFromPort:11000]; + + WebsocketSingleton *websocket = [WebsocketSingleton sharedInstance]; + websocket.socketPort = self.dataPort; + websocket.socketIOSecret = self.socketIOSecret; uint16_t socksPort = [findFreePort getFirstStartingFromPort:arc4random_uniform(65000 - 1024) + 1024]; uint16_t controlPort = [findFreePort getFirstStartingFromPort:arc4random_uniform(65000 - 1024) + 1024]; uint16_t httpTunnelPort = [findFreePort getFirstStartingFromPort:arc4random_uniform(65000 - 1024) + 1024]; - // (2/6) Spawn tor with proper configuration + // (2/4) Spawn tor with proper configuration self.tor = [TorHandler new]; @@ -91,7 +81,7 @@ - (void) spinupBackend:(BOOL)init { }); - // (4/6) Connect to tor control port natively (so we can use it to shutdown tor when app goes idle) + // (3/4) Connect to tor control port natively (so we can use it to shutdown tor when app goes idle) dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{ NSData *authCookieData = [self getAuthCookieData]; @@ -110,14 +100,7 @@ - (void) spinupBackend:(BOOL)init { }]; }); - - // (5/6) Update data port information and broadcast it to frontend - if (init) { - [self initWebsocketConnection]; - } - - - // (6/6) Launch backend or rewire services + // (4/4) Launch backend or rewire services dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ diff --git a/packages/mobile/ios/WebsocketSingleton.swift b/packages/mobile/ios/WebsocketSingleton.swift new file mode 100644 index 0000000000..604ef7d136 --- /dev/null +++ b/packages/mobile/ios/WebsocketSingleton.swift @@ -0,0 +1,9 @@ +@objcMembers +class WebsocketSingleton: NSObject { + static let sharedInstance = WebsocketSingleton() + + var socketPort: UInt16 = 0 + var socketIOSecret: String = "" + + private override init() {} +} From 00a060561b736214d21ce2c3ffe68a21fa133a8d Mon Sep 17 00:00:00 2001 From: Isla Koenigsknecht Date: Mon, 6 May 2024 14:39:05 -0400 Subject: [PATCH 23/48] Fix(2424): Fix issue with connecting on resume on ios (#2506) * Fix issue with connecting on resume on ios * Update changelogs --- CHANGELOG.md | 2 +- packages/backend/src/backendManager.ts | 4 +- .../connections-manager.service.tor.spec.ts | 5 +- .../connections-manager.service.ts | 20 ++++++ .../backend/src/nest/libp2p/libp2p.service.ts | 68 +++++++++++++++++-- .../backend/src/nest/libp2p/libp2p.types.ts | 10 +++ packages/mobile/CHANGELOG.md | 2 +- .../mobile/ios/NodeJsMobile/NodeRunner.mm | 2 +- packages/mobile/ios/Podfile.lock | 27 ++++---- .../ios/Quiet.xcodeproj/project.pbxproj | 2 +- .../startConnection/startConnection.saga.ts | 6 +- 11 files changed, 122 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dcc0e8492c..3512ab6d81 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,9 +21,9 @@ # Fixes: -* Allow JPEG and GIF files as profile photos ([#2332](https://github.com/TryQuiet/quiet/issues/2332)) * Fix issues with recreating general channel when deleted while offline ([#2334](https://github.com/TryQuiet/quiet/issues/2334)) * Fix package.json license inconsistency +* Fixes issue with reconnecting to peers on resume on iOS ([#2424](https://github.com/TryQuiet/quiet/issues/2424)) [2.1.2] diff --git a/packages/backend/src/backendManager.ts b/packages/backend/src/backendManager.ts index 42fa25d78b..63fd7449be 100644 --- a/packages/backend/src/backendManager.ts +++ b/packages/backend/src/backendManager.ts @@ -111,7 +111,7 @@ export const runBackendMobile = async () => { rn_bridge.channel.on('close', async () => { const connectionsManager = app.get(ConnectionsManagerService) - connectionsManager.closeSocket() + await connectionsManager.pause() }) rn_bridge.channel.on('open', async (msg: OpenServices) => { @@ -123,7 +123,7 @@ export const runBackendMobile = async () => { torControl.torControlParams.auth.value = msg.authCookie proxyAgent.proxy.port = msg.httpTunnelPort - await connectionsManager.openSocket() + await connectionsManager.resume() }) } diff --git a/packages/backend/src/nest/connections-manager/connections-manager.service.tor.spec.ts b/packages/backend/src/nest/connections-manager/connections-manager.service.tor.spec.ts index 98d717762b..99567d5520 100644 --- a/packages/backend/src/nest/connections-manager/connections-manager.service.tor.spec.ts +++ b/packages/backend/src/nest/connections-manager/connections-manager.service.tor.spec.ts @@ -138,7 +138,10 @@ describe('Connections manager', () => { // Peer connected await connectionsManagerService.init() - libp2pService.connectedPeers.set(peerId.toString(), DateTime.utc().valueOf()) + libp2pService.connectedPeers.set(peerId.toString(), { + connectedAtSeconds: DateTime.utc().valueOf(), + address: peerId.toString(), + }) // Peer disconnected const remoteAddr = `${peerId.toString()}` diff --git a/packages/backend/src/nest/connections-manager/connections-manager.service.ts b/packages/backend/src/nest/connections-manager/connections-manager.service.ts index 6d0e047dc7..c07e2e727a 100644 --- a/packages/backend/src/nest/connections-manager/connections-manager.service.ts +++ b/packages/backend/src/nest/connections-manager/connections-manager.service.ts @@ -47,6 +47,7 @@ import { type UserProfilesStoredEvent, } from '@quiet/types' import { CONFIG_OPTIONS, QUIET_DIR, SERVER_IO_PROVIDER, SOCKS_PROXY_AGENT } from '../const' +import { Libp2pPeerInfo } from '../libp2p/libp2p.types' import { ConfigOptions, GetPorts, ServerIoProviderTypes } from '../types' import { SocketService } from '../socket/socket.service' import { RegistrationService } from '../registration/registration.service' @@ -72,6 +73,7 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI public libp2pService: Libp2pService private ports: GetPorts isTorInit: TorInitState = TorInitState.NOT_STARTED + private peerInfo: Libp2pPeerInfo | undefined = undefined private readonly logger = Logger(ConnectionsManagerService.name) constructor( @@ -246,6 +248,23 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI this.serverIoProvider.io.close() } + public async pause() { + this.logger('Pausing!') + this.logger('Closing socket!') + this.closeSocket() + this.logger('Pausing libp2pService!') + this.peerInfo = await this.libp2pService?.pause() + this.logger('Found the following peer info on pause: ', this.peerInfo) + } + + public async resume() { + this.logger('Resuming!') + this.logger('Reopening socket!') + await this.openSocket() + this.logger('Dialing peers with info: ', this.peerInfo) + await this.libp2pService?.redialPeers(this.peerInfo) + } + // This method is only used on iOS through rn-bridge for reacting on lifecycle changes public async openSocket() { await this.socketService.init() @@ -585,6 +604,7 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI // Update Frontend with Initialized Communities if (this.communityId) { this.serverIoProvider.io.emit(SocketActionTypes.COMMUNITY_LAUNCHED, { id: this.communityId }) + console.log('this.libp2pService.dialedPeers', this.libp2pService.dialedPeers) console.log('this.libp2pService.connectedPeers', this.libp2pService.connectedPeers) console.log('this.libp2pservice', this.libp2pService) this.serverIoProvider.io.emit( diff --git a/packages/backend/src/nest/libp2p/libp2p.service.ts b/packages/backend/src/nest/libp2p/libp2p.service.ts index c4129e9888..00a3763f8a 100644 --- a/packages/backend/src/nest/libp2p/libp2p.service.ts +++ b/packages/backend/src/nest/libp2p/libp2p.service.ts @@ -20,7 +20,7 @@ import { SERVER_IO_PROVIDER, SOCKS_PROXY_AGENT } from '../const' import { ServerIoProviderTypes } from '../types' import { webSockets } from '../websocketOverTor' import { all } from '../websocketOverTor/filters' -import { Libp2pEvents, Libp2pNodeParams } from './libp2p.types' +import { Libp2pConnectedPeer, Libp2pEvents, Libp2pNodeParams, Libp2pPeerInfo } from './libp2p.types' import { ProcessInChunksService } from './process-in-chunks.service' const KEY_LENGTH = 32 @@ -29,7 +29,7 @@ export const LIBP2P_PSK_METADATA = '/key/swarm/psk/1.0.0/\n/base16/\n' @Injectable() export class Libp2pService extends EventEmitter { public libp2pInstance: Libp2p | null - public connectedPeers: Map = new Map() + public connectedPeers: Map = new Map() public dialedPeers: Set = new Set() private readonly logger = Logger(Libp2pService.name) constructor( @@ -48,6 +48,21 @@ export class Libp2pService extends EventEmitter { await this.libp2pInstance?.dial(multiaddr(peerAddress)) } + public getCurrentPeerInfo = (): Libp2pPeerInfo => { + return { + dialed: Array.from(this.dialedPeers), + connected: Array.from(this.connectedPeers.values()).map(peer => peer.address), + } + } + + public pause = async (): Promise => { + const peerInfo = this.getCurrentPeerInfo() + await this.hangUpPeers(peerInfo.dialed) + this.dialedPeers.clear() + this.connectedPeers.clear() + return peerInfo + } + public readonly createLibp2pAddress = (address: string, peerId: string): string => { return createLibp2pAddress(address, peerId) } @@ -74,6 +89,47 @@ export class Libp2pService extends EventEmitter { return { psk: psk.toString('base64'), fullKey } } + public async hangUpPeers(peers: string[]) { + this.logger('Hanging up on all peers') + for (const peer of peers) { + await this.hangUpPeer(peer) + } + } + + public async hangUpPeer(peerAddress: string) { + this.logger('Hanging up on peer', peerAddress) + await this.libp2pInstance?.hangUp(multiaddr(peerAddress)) + this.dialedPeers.delete(peerAddress) + this.connectedPeers.delete(peerAddress) + } + + /** + * Hang up existing peer connections and re-dial them. Specifically useful on + * iOS where Tor receives a new port when the app resumes from background and + * we want to close/re-open connections. + */ + public async redialPeers(peerInfo?: Libp2pPeerInfo) { + const dialed = peerInfo ? peerInfo.dialed : Array.from(this.dialedPeers) + const toDial = peerInfo + ? [...peerInfo.connected, ...peerInfo.dialed] + : [...this.connectedPeers.keys(), ...this.dialedPeers] + + if (dialed.length === 0) { + this.logger('No peers to redial!') + return + } + + this.logger(`Re-dialing ${dialed.length} peers`) + + // TODO: Sort peers + for (const peerAddress of dialed) { + await this.hangUpPeer(peerAddress) + } + + this.processInChunksService.updateData(toDial) + await this.processInChunksService.process() + } + public async createInstance(params: Libp2pNodeParams): Promise { if (this.libp2pInstance) { return this.libp2pInstance @@ -157,7 +213,11 @@ export class Libp2pService extends EventEmitter { const localPeerId = peerId.toString() this.logger(`${localPeerId} connected to ${remotePeerId}`) - this.connectedPeers.set(remotePeerId, DateTime.utc().valueOf()) + const connectedPeer: Libp2pConnectedPeer = { + address: peer.detail.remoteAddr.toString(), + connectedAtSeconds: DateTime.utc().valueOf(), + } + this.connectedPeers.set(remotePeerId, connectedPeer) this.logger(`${localPeerId} is connected to ${this.connectedPeers.size} peers`) this.logger(`${localPeerId} has ${this.libp2pInstance?.getConnections().length} open connections`) @@ -176,7 +236,7 @@ export class Libp2pService extends EventEmitter { } this.logger(`${localPeerId} has ${this.libp2pInstance.getConnections().length} open connections`) - const connectionStartTime = this.connectedPeers.get(remotePeerId) + const connectionStartTime: number = this.connectedPeers.get(remotePeerId)!.connectedAtSeconds if (!connectionStartTime) { this.logger.error(`No connection start time for peer ${remotePeerId}`) return diff --git a/packages/backend/src/nest/libp2p/libp2p.types.ts b/packages/backend/src/nest/libp2p/libp2p.types.ts index 985c3a4907..34b7056db2 100644 --- a/packages/backend/src/nest/libp2p/libp2p.types.ts +++ b/packages/backend/src/nest/libp2p/libp2p.types.ts @@ -17,3 +17,13 @@ export interface Libp2pNodeParams { peers: string[] psk: Uint8Array } + +export type Libp2pPeerInfo = { + dialed: string[] + connected: string[] +} + +export type Libp2pConnectedPeer = { + address: string + connectedAtSeconds: number +} diff --git a/packages/mobile/CHANGELOG.md b/packages/mobile/CHANGELOG.md index dcc0e8492c..3512ab6d81 100644 --- a/packages/mobile/CHANGELOG.md +++ b/packages/mobile/CHANGELOG.md @@ -21,9 +21,9 @@ # Fixes: -* Allow JPEG and GIF files as profile photos ([#2332](https://github.com/TryQuiet/quiet/issues/2332)) * Fix issues with recreating general channel when deleted while offline ([#2334](https://github.com/TryQuiet/quiet/issues/2334)) * Fix package.json license inconsistency +* Fixes issue with reconnecting to peers on resume on iOS ([#2424](https://github.com/TryQuiet/quiet/issues/2424)) [2.1.2] diff --git a/packages/mobile/ios/NodeJsMobile/NodeRunner.mm b/packages/mobile/ios/NodeJsMobile/NodeRunner.mm index 413025e536..35f909e793 100644 --- a/packages/mobile/ios/NodeJsMobile/NodeRunner.mm +++ b/packages/mobile/ios/NodeJsMobile/NodeRunner.mm @@ -205,7 +205,7 @@ - (void) startEngineWithArguments:(NSArray*)arguments:(NSString*)builtinModulesP nodePath = [nodePath stringByAppendingString:builtinModulesPath]; } setenv([@"NODE_PATH" UTF8String], (const char*)[nodePath UTF8String], 1); - setenv([@"DEBUG" UTF8String], "backend:*", 1); + setenv([@"DEBUG" UTF8String], "backend:*,state-manager:*,libp2p:pnet", 1); int c_arguments_size=0; diff --git a/packages/mobile/ios/Podfile.lock b/packages/mobile/ios/Podfile.lock index e550809b7d..0041f1c559 100644 --- a/packages/mobile/ios/Podfile.lock +++ b/packages/mobile/ios/Podfile.lock @@ -15,15 +15,18 @@ PODS: - hermes-engine/Pre-built (= 0.73.2) - hermes-engine/Pre-built (0.73.2) - libevent (2.1.12) - - libwebp (1.2.3): - - libwebp/demux (= 1.2.3) - - libwebp/mux (= 1.2.3) - - libwebp/webp (= 1.2.3) - - libwebp/demux (1.2.3): + - libwebp (1.3.2): + - libwebp/demux (= 1.3.2) + - libwebp/mux (= 1.3.2) + - libwebp/sharpyuv (= 1.3.2) + - libwebp/webp (= 1.3.2) + - libwebp/demux (1.3.2): - libwebp/webp - - libwebp/mux (1.2.3): + - libwebp/mux (1.3.2): - libwebp/demux - - libwebp/webp (1.2.3) + - libwebp/sharpyuv (1.3.2) + - libwebp/webp (1.3.2): + - libwebp/sharpyuv - RCT-Folly (2022.05.16.00): - boost - DoubleConversion @@ -1323,14 +1326,14 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: boost: d3f49c53809116a5d38da093a8aa78bf551aed09 - DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54 + DoubleConversion: fea03f2699887d960129cc54bba7e52542b6f953 FBLazyVector: fbc4957d9aa695250b55d879c1d86f79d7e69ab4 FBReactNativeSpec: 86de768f89901ef6ed3207cd686362189d64ac88 fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9 - glog: 04b94705f318337d7ead9e6d17c019bd9b1f6b1b + glog: c5d68082e772fa1c511173d6b30a9de2c05a69a2 hermes-engine: b361c9ef5ef3cda53f66e195599b47e1f84ffa35 libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913 - libwebp: 60305b2e989864154bd9be3d772730f08fc6a59c + libwebp: 1786c9f4ff8a279e4dac1e8f385004d5fc253009 RCT-Folly: 7169b2b1c44399c76a47b5deaaba715eeeb476c0 RCTRequired: 9b1e7e262745fb671e33c51c1078d093bd30e322 RCTTypeSafety: a759e3b086eccf3e2cbf2493d22f28e082f958e6 @@ -1391,8 +1394,8 @@ SPEC CHECKSUMS: SDWebImageWebPCoder: 908b83b6adda48effe7667cd2b7f78c897e5111d SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17 Tor: 39dc71bf048312e202608eb499ca5c74e841b503 - Yoga: 13c8ef87792450193e117976337b8527b49e8c03 + Yoga: e64aa65de36c0832d04e8c7bd614396c77a80047 PODFILE CHECKSUM: 811e75c5d23ebd5b5f3e16c6dd4bae230db730d0 -COCOAPODS: 1.14.3 +COCOAPODS: 1.13.0 diff --git a/packages/mobile/ios/Quiet.xcodeproj/project.pbxproj b/packages/mobile/ios/Quiet.xcodeproj/project.pbxproj index 1f0cee5e40..a0020c81b9 100644 --- a/packages/mobile/ios/Quiet.xcodeproj/project.pbxproj +++ b/packages/mobile/ios/Quiet.xcodeproj/project.pbxproj @@ -4975,7 +4975,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "APP_PATH=\"${TARGET_BUILD_DIR}/${WRAPPER_NAME}\"\n\n# This script loops through the frameworks embedded in the application and\n# removes unused architectures.\nfind \"$APP_PATH\" -name '*.framework' -type d | while read -r FRAMEWORK\ndo\n FRAMEWORK_EXECUTABLE_NAME=$(defaults read \"$FRAMEWORK/Info.plist\" CFBundleExecutable)\n FRAMEWORK_EXECUTABLE_PATH=\"$FRAMEWORK/$FRAMEWORK_EXECUTABLE_NAME\"\n echo \"Executable is $FRAMEWORK_EXECUTABLE_PATH\"\n\n EXTRACTED_ARCHS=()\n\n for ARCH in $ARCHS\n do\n echo \"Extracting $ARCH from $FRAMEWORK_EXECUTABLE_NAME\"\n lipo -extract \"$ARCH\" \"$FRAMEWORK_EXECUTABLE_PATH\" -o \"$FRAMEWORK_EXECUTABLE_PATH-$ARCH\"\n EXTRACTED_ARCHS+=(\"$FRAMEWORK_EXECUTABLE_PATH-$ARCH\")\n done\n\n echo \"Merging extracted architectures: ${ARCHS}\"\n lipo -o \"$FRAMEWORK_EXECUTABLE_PATH-merged\" -create \"${EXTRACTED_ARCHS[@]}\"\n rm \"${EXTRACTED_ARCHS[@]}\"\n\n echo \"Replacing original executable with thinned version\"\n rm \"$FRAMEWORK_EXECUTABLE_PATH\"\n mv \"$FRAMEWORK_EXECUTABLE_PATH-merged\" \"$FRAMEWORK_EXECUTABLE_PATH\"\n\ndone\n"; + shellScript = "APP_PATH=\"${TARGET_BUILD_DIR}/${WRAPPER_NAME}\"\n\n# This script loops through the frameworks embedded in the application and\n# removes unused architectures.\nfind \"$APP_PATH\" -name '*.framework' -type d ! -name 'hermes.framework' | while read -r FRAMEWORK\ndo\n FRAMEWORK_EXECUTABLE_NAME=$(defaults read \"$FRAMEWORK/Info.plist\" CFBundleExecutable)\n FRAMEWORK_EXECUTABLE_PATH=\"$FRAMEWORK/$FRAMEWORK_EXECUTABLE_NAME\"\n echo \"Executable is $FRAMEWORK_EXECUTABLE_PATH\"\n\n EXTRACTED_ARCHS=()\n\n for ARCH in $ARCHS\n do\n echo \"Extracting $ARCH from $FRAMEWORK_EXECUTABLE_NAME\"\n lipo -extract \"$ARCH\" \"$FRAMEWORK_EXECUTABLE_PATH\" -o \"$FRAMEWORK_EXECUTABLE_PATH-$ARCH\"\n EXTRACTED_ARCHS+=(\"$FRAMEWORK_EXECUTABLE_PATH-$ARCH\")\n done\n\n echo \"Merging extracted architectures: ${ARCHS}\"\n lipo -o \"$FRAMEWORK_EXECUTABLE_PATH-merged\" -create \"${EXTRACTED_ARCHS[@]}\"\n rm \"${EXTRACTED_ARCHS[@]}\"\n\n echo \"Replacing original executable with thinned version\"\n rm \"$FRAMEWORK_EXECUTABLE_PATH\"\n mv \"$FRAMEWORK_EXECUTABLE_PATH-merged\" \"$FRAMEWORK_EXECUTABLE_PATH\"\n\ndone\n"; }; 18FD2A32296D736300A2B8C0 /* [CUSTOM NODEJS MOBILE] Remove Python3 Binaries */ = { isa = PBXShellScriptBuildPhase; diff --git a/packages/mobile/src/store/init/startConnection/startConnection.saga.ts b/packages/mobile/src/store/init/startConnection/startConnection.saga.ts index e9f9a220ae..a303a237b9 100644 --- a/packages/mobile/src/store/init/startConnection/startConnection.saga.ts +++ b/packages/mobile/src/store/init/startConnection/startConnection.saga.ts @@ -67,8 +67,8 @@ function subscribeSocketLifecycle(socket: Socket, socketIOData: WebsocketConnect console.log('client: Websocket connected', socket_id) emit(initActions.setWebsocketConnected(socketIOData)) }) - socket.on('disconnect', () => { - console.log('client: Closing socket connection', socket_id) + socket.on('disconnect', reason => { + console.warn('client: Closing socket connection', socket_id, reason) emit(initActions.suspendWebsocketConnection()) }) return () => {} @@ -76,7 +76,7 @@ function subscribeSocketLifecycle(socket: Socket, socketIOData: WebsocketConnect } function* cancelRootTaskSaga(task: FixedTask): Generator { - console.log('Canceling root task') + console.warn('Canceling root task', task.error()) yield* cancel(task) yield* put(initActions.canceledRootTask()) } From 47b7550d5b1da25f0f131602fdadc425c622811f Mon Sep 17 00:00:00 2001 From: Wiktor Sieprawski Date: Wed, 24 Apr 2024 13:41:17 +0200 Subject: [PATCH 24/48] chore: improve self hosted runner (Detox) (#2453) * chore: start own metro * chore: comment out restore connection saga usage * chore: repeat websocket connection tries on android * chore: update pods * chore: increase action timeout * fix: always stop metro * chore: limit intervals * chore: enlarge intervals * fix: use callback on react app init * chore: remove redundand sagas * fix: move app_ready event * test: mock native modules --- .../com/quietmobile/Backend/BackendWorker.kt | 40 ++++-------------- .../Communication/CommunicationModule.java | 20 ++++++++- .../java/com/quietmobile/MainApplication.kt | 19 +++++++++ .../main/java/com/quietmobile/Utils/Const.kt | 3 -- .../main/java/com/quietmobile/Utils/Utils.kt | 2 + .../ios/Quiet.xcodeproj/project.pbxproj | 6 ++- packages/mobile/src/App.tsx | 5 ++- .../blindConnection/blindConnection.saga.ts | 15 ------- .../mobile/src/store/init/init.master.saga.ts | 4 +- packages/mobile/src/store/init/init.slice.ts | 1 - .../restoreConnection.saga.test.ts | 41 ------------------- .../restoreConnection.saga.ts | 20 --------- .../startConnection/startConnection.saga.ts | 13 ++++++ .../redirection/redirection.saga.test.ts | 7 ++++ .../redirection/redirection.saga.ts | 8 +++- packages/mobile/src/store/root.saga.ts | 4 +- packages/state-manager/src/constants.ts | 1 + 17 files changed, 82 insertions(+), 127 deletions(-) delete mode 100644 packages/mobile/src/store/init/blindConnection/blindConnection.saga.ts delete mode 100644 packages/mobile/src/store/init/startConnection/restoreConnection/restoreConnection.saga.test.ts delete mode 100644 packages/mobile/src/store/init/startConnection/restoreConnection/restoreConnection.saga.ts diff --git a/packages/mobile/android/app/src/main/java/com/quietmobile/Backend/BackendWorker.kt b/packages/mobile/android/app/src/main/java/com/quietmobile/Backend/BackendWorker.kt index b25ff8ef7f..57ea48a0ee 100644 --- a/packages/mobile/android/app/src/main/java/com/quietmobile/Backend/BackendWorker.kt +++ b/packages/mobile/android/app/src/main/java/com/quietmobile/Backend/BackendWorker.kt @@ -9,14 +9,12 @@ import androidx.core.app.NotificationCompat import androidx.work.CoroutineWorker import androidx.work.ForegroundInfo import androidx.work.WorkerParameters -import com.google.gson.Gson import com.quietmobile.BuildConfig import com.quietmobile.Communication.CommunicationModule +import com.quietmobile.MainApplication import com.quietmobile.Notification.NotificationHandler import com.quietmobile.R -import com.quietmobile.Scheme.WebsocketConnectionPayload import com.quietmobile.Utils.Const -import com.quietmobile.Utils.Const.WEBSOCKET_CONNECTION_DELAY import com.quietmobile.Utils.Utils import com.quietmobile.Utils.isAppOnForeground import io.socket.client.IO @@ -98,9 +96,12 @@ class BackendWorker(private val context: Context, workerParams: WorkerParameters withContext(Dispatchers.IO) { // Get and store data port for usage in methods across the app - val dataPort = Utils.getOpenPort(11000) + val socketPort = Utils.getOpenPort(11000) val socketIOSecret = Utils.generateRandomString(20) + (applicationContext as MainApplication).setSocketPort(socketPort) + (applicationContext as MainApplication).setSocketIOSecret(socketIOSecret) + // Init nodejs project launch { nodeProject.init() @@ -108,21 +109,7 @@ class BackendWorker(private val context: Context, workerParams: WorkerParameters launch { notificationHandler = NotificationHandler(context) - subscribePushNotifications(dataPort, socketIOSecret) - } - - launch { - /* - * Wait for CommunicationModule to be initialized with reactContext - * (there's no callback we can use for that purpose). - * - * Code featured below suspends nothing but the websocket connection - * and it doesn't affect anything besides that. - * - * In any case, websocket won't connect until data server starts listening - */ - delay(WEBSOCKET_CONNECTION_DELAY) - startWebsocketConnection(dataPort, socketIOSecret) + subscribePushNotifications(socketPort, socketIOSecret) } val dataPath = Utils.createDirectory(context) @@ -139,7 +126,7 @@ class BackendWorker(private val context: Context, workerParams: WorkerParameters * https://github.com/TryQuiet/quiet/issues/2214 */ delay(500) - startNodeProjectWithArguments("bundle.cjs --torBinary $torBinary --dataPath $dataPath --dataPort $dataPort --platform $platform --socketIOSecret $socketIOSecret") + startNodeProjectWithArguments("bundle.cjs --torBinary $torBinary --dataPath $dataPath --dataPort $socketPort --platform $platform --socketIOSecret $socketIOSecret") } } @@ -155,8 +142,6 @@ class BackendWorker(private val context: Context, workerParams: WorkerParameters return Result.success() } - private external fun sendMessageToNodeChannel(channelName: String, message: String): Void - private external fun startNodeWithArguments( arguments: Array?, modulesPath: String? @@ -214,17 +199,6 @@ class BackendWorker(private val context: Context, workerParams: WorkerParameters notificationHandler.notify(message, username) } - private fun startWebsocketConnection(port: Int, socketIOSecret: String) { - Log.d("WEBSOCKET CONNECTION", "Starting on $port") - // Proceed only if data port is defined - val websocketConnectionPayload = WebsocketConnectionPayload(port, socketIOSecret) - CommunicationModule.handleIncomingEvents( - CommunicationModule.WEBSOCKET_CONNECTION_CHANNEL, - Gson().toJson(websocketConnectionPayload), - "" // Empty extras - ) - } - fun handleNodeMessages(channelName: String, msg: String?) { print("handle node message - channel name $channelName") print("handle node message - msg $msg") diff --git a/packages/mobile/android/app/src/main/java/com/quietmobile/Communication/CommunicationModule.java b/packages/mobile/android/app/src/main/java/com/quietmobile/Communication/CommunicationModule.java index e51ee1f125..ff186756b6 100644 --- a/packages/mobile/android/app/src/main/java/com/quietmobile/Communication/CommunicationModule.java +++ b/packages/mobile/android/app/src/main/java/com/quietmobile/Communication/CommunicationModule.java @@ -10,7 +10,10 @@ import com.facebook.react.bridge.ReactMethod; import com.facebook.react.bridge.WritableMap; import com.facebook.react.modules.core.RCTNativeAppEventEmitter; +import com.google.gson.Gson; +import com.quietmobile.MainApplication; import com.quietmobile.Notification.NotificationHandler; +import com.quietmobile.Scheme.WebsocketConnectionPayload; import androidx.annotation.NonNull; @@ -24,6 +27,8 @@ public class CommunicationModule extends ReactContextBaseJavaModule { + public static final String APP_READY_CHANNEL = "_APP_READY_"; + public static final String PUSH_NOTIFICATION_CHANNEL = "_PUSH_NOTIFICATION_"; public static final String WEBSOCKET_CONNECTION_CHANNEL = "_WEBSOCKET_CONNECTION_"; public static final String INIT_CHECK_CHANNEL = "_INIT_CHECK_"; @@ -48,14 +53,16 @@ public CommunicationModule(ReactApplicationContext reactContext) { } @ReactMethod - public static void handleIncomingEvents(String event, String payload, String extra) { + public static void handleIncomingEvents(String event, @Nullable String payload, @Nullable String extra) { switch (event) { + case APP_READY_CHANNEL: + startWebsocketConnection(); + break; case PUSH_NOTIFICATION_CHANNEL: String message = payload; String username = extra; notificationHandler.notify(message, username); break; - case WEBSOCKET_CONNECTION_CHANNEL: case INIT_CHECK_CHANNEL: case BACKEND_CLOSED_CHANNEL: passDataToReact(event, payload); @@ -88,6 +95,15 @@ private static void sendEvent(@Nullable WritableMap params) { } } + private static void startWebsocketConnection() { + Context context = reactContext.getApplicationContext(); + int port = ((MainApplication) context).getSocketPort(); + String socketIOSecret = ((MainApplication) context).getSocketIOSecret(); + + WebsocketConnectionPayload websocketConnectionPayload = new WebsocketConnectionPayload(port, socketIOSecret); + passDataToReact(WEBSOCKET_CONNECTION_CHANNEL, new Gson().toJson(websocketConnectionPayload)); + } + @ReactMethod private static void deleteBackendData() { Context context = reactContext.getApplicationContext(); diff --git a/packages/mobile/android/app/src/main/java/com/quietmobile/MainApplication.kt b/packages/mobile/android/app/src/main/java/com/quietmobile/MainApplication.kt index 901a31de38..6b29a6d01a 100644 --- a/packages/mobile/android/app/src/main/java/com/quietmobile/MainApplication.kt +++ b/packages/mobile/android/app/src/main/java/com/quietmobile/MainApplication.kt @@ -52,6 +52,25 @@ class MainApplication : Application(), ReactApplication { createNotificationChannel() } + private var socketPort: Int = 0 + private var socketIOSecret: String = "" + + fun getSocketPort(): Int { + return socketPort + } + + fun setSocketPort(value: Int) { + this.socketPort = value + } + + fun getSocketIOSecret(): String { + return socketIOSecret + } + + fun setSocketIOSecret(value: String) { + this.socketIOSecret = value + } + private fun createForegroundServiceNotificationChannel() { // Create the NotificationChannel, but only on API 26+ because // the NotificationChannel class is new and not in the support library diff --git a/packages/mobile/android/app/src/main/java/com/quietmobile/Utils/Const.kt b/packages/mobile/android/app/src/main/java/com/quietmobile/Utils/Const.kt index ae769aa39d..7254e6a0dc 100644 --- a/packages/mobile/android/app/src/main/java/com/quietmobile/Utils/Const.kt +++ b/packages/mobile/android/app/src/main/java/com/quietmobile/Utils/Const.kt @@ -22,7 +22,4 @@ object Const { const val NODEJS_PROJECT_DIR = "nodejs-project" const val NODEJS_BUILTIN_NATIVE_ASSETS_PREFIX = "nodejs-native-assets-" const val NODEJS_TRASH_DIR = "nodejs-project-trash" - - // Websocket - const val WEBSOCKET_CONNECTION_DELAY: Long = 7000 } diff --git a/packages/mobile/android/app/src/main/java/com/quietmobile/Utils/Utils.kt b/packages/mobile/android/app/src/main/java/com/quietmobile/Utils/Utils.kt index 29491191a0..7d38d92c2a 100644 --- a/packages/mobile/android/app/src/main/java/com/quietmobile/Utils/Utils.kt +++ b/packages/mobile/android/app/src/main/java/com/quietmobile/Utils/Utils.kt @@ -30,6 +30,7 @@ object Utils { return dataDirectory.absolutePath } + @JvmStatic fun generateRandomString(length: Int): String { val CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" val secureRandom = SecureRandom() @@ -43,6 +44,7 @@ object Utils { return randomString.toString() } + @JvmStatic suspend fun getOpenPort(starting: Int) = suspendCoroutine { continuation -> val port = checkPort(starting) continuation.resume(port) diff --git a/packages/mobile/ios/Quiet.xcodeproj/project.pbxproj b/packages/mobile/ios/Quiet.xcodeproj/project.pbxproj index a0020c81b9..957126e16a 100644 --- a/packages/mobile/ios/Quiet.xcodeproj/project.pbxproj +++ b/packages/mobile/ios/Quiet.xcodeproj/project.pbxproj @@ -5486,7 +5486,8 @@ OTHER_CPLUSPLUSFLAGS = "$(inherited)"; OTHER_LDFLAGS = ( "$(inherited)", - " ", + "-Wl", + "-ld_classic", ); REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; @@ -5551,7 +5552,8 @@ OTHER_CPLUSPLUSFLAGS = "$(inherited)"; OTHER_LDFLAGS = ( "$(inherited)", - " ", + "-Wl", + "-ld_classic", ); REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; diff --git a/packages/mobile/src/App.tsx b/packages/mobile/src/App.tsx index f5128ddc39..4a2666ddfd 100644 --- a/packages/mobile/src/App.tsx +++ b/packages/mobile/src/App.tsx @@ -1,7 +1,9 @@ import React, { useEffect } from 'react' import { useDispatch } from 'react-redux' -import { LogBox, StatusBar } from 'react-native' +import { LogBox, NativeModules, StatusBar } from 'react-native' + +import { APP_READY_CHANNEL } from '@quiet/state-manager' import WebviewCrypto from 'react-native-webview-crypto' @@ -28,7 +30,6 @@ import { NavigationContainer } from '@react-navigation/native' import { createNativeStackNavigator } from '@react-navigation/native-stack' import { navigationRef } from './RootNavigation' -import { initActions } from './store/init/init.slice' import { navigationActions } from './store/navigation/navigation.slice' import { rootSaga } from './store/root.saga' diff --git a/packages/mobile/src/store/init/blindConnection/blindConnection.saga.ts b/packages/mobile/src/store/init/blindConnection/blindConnection.saga.ts deleted file mode 100644 index 44571067c0..0000000000 --- a/packages/mobile/src/store/init/blindConnection/blindConnection.saga.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { select, put } from 'typed-redux-saga' -import { initSelectors } from '../init.selectors' -import { initActions } from '../init.slice' - -export function* blindConnectionSaga(): Generator { - const isWebsocketConnected = yield* select(initSelectors.isWebsocketConnected) - const lastKnownSocketIOData = yield* select(initSelectors.lastKnownSocketIOData) - - console.log('WEBSOCKET', 'Entered blind connection saga', isWebsocketConnected, lastKnownSocketIOData) - - if (!isWebsocketConnected && lastKnownSocketIOData.dataPort !== 0) { - console.log('WEBSOCKET', 'Hooking up blindly at last known data port: ', lastKnownSocketIOData.dataPort) - yield* put(initActions.startWebsocketConnection(lastKnownSocketIOData)) - } -} diff --git a/packages/mobile/src/store/init/init.master.saga.ts b/packages/mobile/src/store/init/init.master.saga.ts index af5ddec265..82fc31efd1 100644 --- a/packages/mobile/src/store/init/init.master.saga.ts +++ b/packages/mobile/src/store/init/init.master.saga.ts @@ -1,12 +1,10 @@ -import { all, takeEvery, takeLatest, takeLeading } from 'typed-redux-saga' +import { all, takeLatest, takeLeading } from 'typed-redux-saga' import { initActions } from './init.slice' -import { blindConnectionSaga } from './blindConnection/blindConnection.saga' import { startConnectionSaga } from './startConnection/startConnection.saga' import { deepLinkSaga } from './deepLink/deepLink.saga' export function* initMasterSaga(): Generator { yield all([ - takeEvery(initActions.blindWebsocketConnection.type, blindConnectionSaga), takeLatest(initActions.startWebsocketConnection.type, startConnectionSaga), takeLeading(initActions.deepLink.type, deepLinkSaga), ]) diff --git a/packages/mobile/src/store/init/init.slice.ts b/packages/mobile/src/store/init/init.slice.ts index f256964c22..f9106199d6 100644 --- a/packages/mobile/src/store/init/init.slice.ts +++ b/packages/mobile/src/store/init/init.slice.ts @@ -60,7 +60,6 @@ export const initSlice = createSlice({ id: event, }) }, - blindWebsocketConnection: state => state, startWebsocketConnection: (state, _action: PayloadAction) => state, suspendWebsocketConnection: state => { state.isWebsocketConnected = false diff --git a/packages/mobile/src/store/init/startConnection/restoreConnection/restoreConnection.saga.test.ts b/packages/mobile/src/store/init/startConnection/restoreConnection/restoreConnection.saga.test.ts deleted file mode 100644 index c9fe7cc4dd..0000000000 --- a/packages/mobile/src/store/init/startConnection/restoreConnection/restoreConnection.saga.test.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { socket } from '@quiet/state-manager' -import { combineReducers } from '@reduxjs/toolkit' -import { expectSaga } from 'redux-saga-test-plan' -import { StoreKeys } from '../../../store.keys' -import { initActions, initReducer, InitState } from '../../init.slice' -import { restoreConnectionSaga } from './restoreConnection.saga' - -describe('restoreConnectionSaga', () => { - test('do nothing if connection is already started', async () => { - const socketIOData = { - dataPort: 9477, - socketIOSecret: 'secret', - } - await expectSaga(restoreConnectionSaga) - .withReducer(combineReducers({ [StoreKeys.Init]: initReducer }), { - [StoreKeys.Init]: { - ...new InitState(), - isWebsocketConnected: true, - lastKnownSocketIOData: socketIOData, - }, - }) - .not.put(initActions.startWebsocketConnection(socketIOData)) - .run() - }) - test('do nothing if last known data port is not set', async () => { - const socketIOData = { - dataPort: 0, - socketIOSecret: 'secret', - } - await expectSaga(restoreConnectionSaga) - .withReducer(combineReducers({ [StoreKeys.Init]: initReducer }), { - [StoreKeys.Init]: { - ...new InitState(), - isWebsocketConnected: false, - lastKnownSocketIOData: socketIOData, - }, - }) - .not.put(initActions.startWebsocketConnection(socketIOData)) - .run() - }) -}) diff --git a/packages/mobile/src/store/init/startConnection/restoreConnection/restoreConnection.saga.ts b/packages/mobile/src/store/init/startConnection/restoreConnection/restoreConnection.saga.ts deleted file mode 100644 index 3a7c001360..0000000000 --- a/packages/mobile/src/store/init/startConnection/restoreConnection/restoreConnection.saga.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { delay, put, select } from 'typed-redux-saga' -import { initSelectors } from '../../init.selectors' -import { initActions } from '../../init.slice' - -const WEBSOCKET_CONNECTION_DELAY = 15000 - -export function* restoreConnectionSaga(): Generator { - // Give the worker time to init websocket connection - yield* delay(WEBSOCKET_CONNECTION_DELAY + 1000) - - const isWebsocketConnected = yield* select(initSelectors.isWebsocketConnected) - const socketIOData = yield* select(initSelectors.lastKnownSocketIOData) - - console.log('WEBSOCKET', 'Entered restore connection saga', isWebsocketConnected, socketIOData) - - if (!isWebsocketConnected && socketIOData.dataPort !== 0) { - console.log('WEBSOCKET', 'Restoring connection with data port: ', socketIOData.dataPort) - yield* put(initActions.startWebsocketConnection(socketIOData)) - } -} diff --git a/packages/mobile/src/store/init/startConnection/startConnection.saga.ts b/packages/mobile/src/store/init/startConnection/startConnection.saga.ts index a303a237b9..4cc55f53f9 100644 --- a/packages/mobile/src/store/init/startConnection/startConnection.saga.ts +++ b/packages/mobile/src/store/init/startConnection/startConnection.saga.ts @@ -11,6 +11,19 @@ import { SocketActionTypes } from '@quiet/types' export function* startConnectionSaga( action: PayloadAction['payload']> ): Generator { + const isAlreadyConnected = yield* select(initSelectors.isWebsocketConnected) + if (isAlreadyConnected) return + + while (true) { + const isCryptoEngineInitialized = yield* select(initSelectors.isCryptoEngineInitialized) + console.log('WEBSOCKET', 'Waiting for crypto engine to initialize') + if (!isCryptoEngineInitialized) { + yield* delay(500) + } else { + break + } + } + const { dataPort, socketIOSecret } = action.payload console.log('WEBSOCKET', 'Entered start connection saga', dataPort) diff --git a/packages/mobile/src/store/navigation/redirection/redirection.saga.test.ts b/packages/mobile/src/store/navigation/redirection/redirection.saga.test.ts index 6d8dab20d0..66ea6b5496 100644 --- a/packages/mobile/src/store/navigation/redirection/redirection.saga.test.ts +++ b/packages/mobile/src/store/navigation/redirection/redirection.saga.test.ts @@ -12,11 +12,18 @@ import { reducers } from '../../root.reducer' import { redirectionSaga } from './redirection.saga' import { initActions } from '../../init/init.slice' +import { NativeModules } from 'react-native' describe('redirectionSaga', () => { let store: Store let factory: FactoryGirl + beforeAll(() => { + NativeModules.CommunicationModule = { + handleIncomingEvents: jest.fn(), + } + }) + beforeEach(async () => { setupCrypto() store = (await prepareStore()).store diff --git a/packages/mobile/src/store/navigation/redirection/redirection.saga.ts b/packages/mobile/src/store/navigation/redirection/redirection.saga.ts index 798ab18390..7c903adfc5 100644 --- a/packages/mobile/src/store/navigation/redirection/redirection.saga.ts +++ b/packages/mobile/src/store/navigation/redirection/redirection.saga.ts @@ -1,12 +1,16 @@ -import { put, take, select } from 'typed-redux-saga' +import { NativeModules } from 'react-native' +import { call, put, take, select } from 'typed-redux-saga' import { initSelectors } from '../../init/init.selectors' import { navigationSelectors } from '../navigation.selectors' import { navigationActions } from '../navigation.slice' import { ScreenNames } from '../../../const/ScreenNames.enum' -import { identity } from '@quiet/state-manager' +import { APP_READY_CHANNEL, identity } from '@quiet/state-manager' import { initActions } from '../../init/init.slice' export function* redirectionSaga(): Generator { + // Let the native modules know to init web socket connection + yield* call(NativeModules.CommunicationModule.handleIncomingEvents, APP_READY_CHANNEL, null, null) + // Do not redirect if user opened the app from url (quiet://) const deepLinking = yield* select(initSelectors.deepLinking) if (deepLinking) { diff --git a/packages/mobile/src/store/root.saga.ts b/packages/mobile/src/store/root.saga.ts index b77b14dfb4..9b2fffff52 100644 --- a/packages/mobile/src/store/root.saga.ts +++ b/packages/mobile/src/store/root.saga.ts @@ -1,4 +1,4 @@ -import { all, takeEvery, fork } from 'typed-redux-saga' +import { all, takeEvery } from 'typed-redux-saga' import { nativeServicesMasterSaga } from './nativeServices/nativeServices.master.saga' import { navigationMasterSaga } from './navigation/navigation.master.saga' import { initMasterSaga } from './init/init.master.saga' @@ -6,7 +6,6 @@ import { initActions } from './init/init.slice' import { setupCryptoSaga } from './init/setupCrypto/setupCrypto.saga' import { publicChannels } from '@quiet/state-manager' import { showNotificationSaga } from './nativeServices/showNotification/showNotification.saga' -import { restoreConnectionSaga } from './init/startConnection/restoreConnection/restoreConnection.saga' export function* rootSaga(): Generator { yield all([ @@ -14,7 +13,6 @@ export function* rootSaga(): Generator { takeEvery(initActions.setStoreReady.type, initMasterSaga), takeEvery(initActions.setStoreReady.type, navigationMasterSaga), takeEvery(initActions.setStoreReady.type, nativeServicesMasterSaga), - fork(restoreConnectionSaga), // Below line is reponsible for displaying notifications about messages from channels other than currently viewing one takeEvery(publicChannels.actions.markUnreadChannel.type, showNotificationSaga), ]) diff --git a/packages/state-manager/src/constants.ts b/packages/state-manager/src/constants.ts index a8f9ac9130..848b3409ca 100644 --- a/packages/state-manager/src/constants.ts +++ b/packages/state-manager/src/constants.ts @@ -1,5 +1,6 @@ export const AUTODOWNLOAD_SIZE_LIMIT = 20971520 // 20 MB +export const APP_READY_CHANNEL = '_APP_READY_' export const PUSH_NOTIFICATION_CHANNEL = '_PUSH_NOTIFICATION_' export const WEBSOCKET_CONNECTION_CHANNEL = '_WEBSOCKET_CONNECTION_' export const INIT_CHECK_CHANNEL = '_INIT_CHECK_' From 2c1112120a018a7c6c885aec08c581413bd770fb Mon Sep 17 00:00:00 2001 From: Isla Koenigsknecht Date: Tue, 7 May 2024 12:29:48 -0400 Subject: [PATCH 25/48] Fix(2496): Random fixes for leave community on mobile (#2498) * Extra logging, blocking actions and random UX error fix * Some more changes * Add more logs * Connect to tor when we should and update peers on connect * Redial when tor is fully ready * More logs, spawn hidden services early again, better hang up and mild refactor of tor * PR fixes * More fixes * Small fix * Fix tests * Fix setup environment on mac in ci * Fix mac e2e filename * Update e2e-mac.yml * Update e2e-mac.yml * Comment fix and better redial on resume * Add back fix I accidentally removed * Don't redial on tor initialization * Update .detoxrc.js * Revert "Update .detoxrc.js" This reverts commit 9290f25a0a8d0b03a3184a11714585fd266822b7. --- .github/actions/setup-env/action.yml | 15 ++++ .github/workflows/e2e-mac.yml | 4 +- .../connections-manager.service.tor.spec.ts | 4 + .../connections-manager.service.ts | 83 ++++++++++++++++--- .../backend/src/nest/libp2p/libp2p.service.ts | 32 ++++--- .../nest/libp2p/process-in-chunks.service.ts | 2 +- .../backend/src/nest/socket/socket.service.ts | 4 +- .../certificates/certificates.store.ts | 7 +- .../src/nest/tor/tor-control.service.ts | 11 ++- packages/backend/src/nest/tor/tor.service.ts | 43 +++++++--- .../src/nest/websocketOverTor/index.ts | 13 +-- packages/mobile/ios/Quiet/Info.plist | 16 ++-- .../src/components/Input/Input.styles.ts | 2 +- .../startConnection/startConnection.saga.ts | 4 +- .../leaveCommunity/leaveCommunity.saga.ts | 17 ++-- .../src/sagas/app/closeServices.saga.ts | 2 +- .../createCommunity/createCommunity.saga.ts | 10 +-- .../channelsReplicated.saga.test.ts | 28 +++---- .../channelsReplicated.saga.ts | 12 +-- packages/types/src/socket.ts | 1 + 20 files changed, 219 insertions(+), 91 deletions(-) diff --git a/.github/actions/setup-env/action.yml b/.github/actions/setup-env/action.yml index 56af1bd3d7..41b4c86715 100644 --- a/.github/actions/setup-env/action.yml +++ b/.github/actions/setup-env/action.yml @@ -15,6 +15,21 @@ runs: - uses: actions/setup-node@master with: node-version: 18.12.1 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + if: runner.os == 'macOS' + with: + python-version: 3.12 + + - name: Print python version + run: which python3 + if: runner.os == 'macOS' + shell: bash + + - name: Install setuptools + run: python3 -m pip install setuptools + shell: bash - name: Print OS name run: echo ${{ runner.os }} diff --git a/.github/workflows/e2e-mac.yml b/.github/workflows/e2e-mac.yml index 97dcbdfe00..880e627678 100644 --- a/.github/workflows/e2e-mac.yml +++ b/.github/workflows/e2e-mac.yml @@ -34,7 +34,7 @@ jobs: - name: FILE_NAME env working-directory: ./packages/desktop/dist - run: echo "FILE_NAME="Quiet-$VERSION.dmg"" >> $GITHUB_ENV + run: echo "FILE_NAME="Quiet-$VERSION-arm64.dmg"" >> $GITHUB_ENV - name: Chmod working-directory: ./packages/desktop/dist @@ -45,7 +45,7 @@ jobs: run: hdiutil mount $FILE_NAME - name: Add App file to applications - run: cd ~ && cp -R "/Volumes/Quiet $VERSION/Quiet.app" /Applications + run: cd ~ && cp -R "/Volumes/Quiet $VERSION-arm64/Quiet.app" /Applications - name: Run invitation link test - Includes 2 separate application clients uses: nick-fields/retry@14672906e672a08bd6eeb15720e9ed3ce869cdd4 # v2.9.0 diff --git a/packages/backend/src/nest/connections-manager/connections-manager.service.tor.spec.ts b/packages/backend/src/nest/connections-manager/connections-manager.service.tor.spec.ts index 99567d5520..4506ed02d8 100644 --- a/packages/backend/src/nest/connections-manager/connections-manager.service.tor.spec.ts +++ b/packages/backend/src/nest/connections-manager/connections-manager.service.tor.spec.ts @@ -138,6 +138,10 @@ describe('Connections manager', () => { // Peer connected await connectionsManagerService.init() + await connectionsManagerService.launchCommunity({ + community, + network: { peerId: userIdentity.peerId, hiddenService: userIdentity.hiddenService }, + }) libp2pService.connectedPeers.set(peerId.toString(), { connectedAtSeconds: DateTime.utc().valueOf(), address: peerId.toString(), diff --git a/packages/backend/src/nest/connections-manager/connections-manager.service.ts b/packages/backend/src/nest/connections-manager/connections-manager.service.ts index c07e2e727a..586bc941a4 100644 --- a/packages/backend/src/nest/connections-manager/connections-manager.service.ts +++ b/packages/backend/src/nest/connections-manager/connections-manager.service.ts @@ -65,6 +65,7 @@ import Logger from '../common/logger' import { emitError } from '../socket/socket.errors' import { createLibp2pAddress, isPSKcodeValid } from '@quiet/common' import { CertFieldsTypes, createRootCA, getCertFieldValue, loadCertificate } from '@quiet/identity' +import { DateTime } from 'luxon' @Injectable() export class ConnectionsManagerService extends EventEmitter implements OnModuleInit { @@ -224,7 +225,10 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI public async closeAllServices(options: { saveTor: boolean } = { saveTor: false }) { if (this.tor && !options.saveTor) { + this.logger('Killing tor') await this.tor.kill() + } else if (options.saveTor) { + this.logger('Saving tor') } if (this.storageService) { this.logger('Stopping orbitdb') @@ -261,8 +265,20 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI this.logger('Resuming!') this.logger('Reopening socket!') await this.openSocket() - this.logger('Dialing peers with info: ', this.peerInfo) - await this.libp2pService?.redialPeers(this.peerInfo) + this.logger('Attempting to redial peers!') + if (this.peerInfo && (this.peerInfo?.connected.length !== 0 || this.peerInfo?.dialed.length !== 0)) { + this.logger('Dialing peers with info from pause: ', this.peerInfo) + await this.libp2pService?.redialPeers([...this.peerInfo.connected, ...this.peerInfo.dialed]) + } else { + this.logger('Dialing peers from stored community (if exists)') + const community = await this.localDbService.getCurrentCommunity() + if (!community) { + this.logger(`No community launched, can't redial`) + return + } + const sortedPeers = await this.localDbService.getSortedPeers(community.peerList ?? []) + await this.libp2pService?.redialPeers(sortedPeers) + } } // This method is only used on iOS through rn-bridge for reacting on lifecycle changes @@ -270,15 +286,34 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI await this.socketService.init() } - public async leaveCommunity() { + public async leaveCommunity(): Promise { + this.logger('Running leaveCommunity') + + this.logger('Resetting tor') this.tor.resetHiddenServices() + + this.logger('Closing the socket') this.closeSocket() + + this.logger('Purging local DB') await this.localDbService.purge() + + this.logger('Closing services') await this.closeAllServices({ saveTor: true }) + + this.logger('Purging data') await this.purgeData() + + this.logger('Resetting state') await this.resetState() + + this.logger('Reopening local DB') await this.localDbService.open() - await this.socketService.init() + + this.logger('Restarting socket') + await this.openSocket() + + return true } async resetState() { @@ -296,16 +331,24 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI i.startsWith('Ipfs') || i.startsWith('OrbitDB') || i.startsWith('backendDB') || i.startsWith('Local Storage') ) for (const dir of dirsToRemove) { - removeFilesFromDir(path.join(this.quietDir, dir)) + const dirPath = path.join(this.quietDir, dir) + this.logger(`Removing dir: ${dirPath}`) + removeFilesFromDir(dirPath) } } public async getNetwork(): Promise { + this.logger('Getting network information') + + this.logger('Creating hidden service') const hiddenService = await this.tor.createNewHiddenService({ targetPort: this.ports.libp2pHiddenService }) + + this.logger('Destroying the hidden service we created') await this.tor.destroyHiddenService(hiddenService.onionAddress.split('.')[0]) // TODO: Do we want to create the PeerId here? It doesn't necessarily have // anything to do with Tor. + this.logger('Getting peer ID') const peerId: PeerId = await PeerId.create() const peerIdJson = peerId.toJSON() this.logger(`Created network for peer ${peerId.toString()}. Address: ${hiddenService.onionAddress}`) @@ -543,8 +586,19 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI await this.libp2pService.createInstance(params) // Libp2p event listeners - this.libp2pService.on(Libp2pEvents.PEER_CONNECTED, (payload: { peers: string[] }) => { + this.libp2pService.on(Libp2pEvents.PEER_CONNECTED, async (payload: { peers: string[] }) => { this.serverIoProvider.io.emit(SocketActionTypes.PEER_CONNECTED, payload) + for (const peer of payload.peers) { + const peerStats: NetworkStats = { + peerId: peer, + connectionTime: 0, + lastSeen: DateTime.utc().toSeconds(), + } + + await this.localDbService.update(LocalDBKeys.PEERS, { + [peer]: peerStats, + }) + } }) this.libp2pService.on(Libp2pEvents.PEER_DISCONNECTED, async (payload: NetworkDataPayload) => { @@ -593,6 +647,11 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI this.tor.on(SocketActionTypes.CONNECTION_PROCESS_INFO, data => { this.serverIoProvider.io.emit(SocketActionTypes.CONNECTION_PROCESS_INFO, data) }) + this.tor.on(SocketActionTypes.REDIAL_PEERS, async data => { + this.logger(`Socket - ${SocketActionTypes.REDIAL_PEERS}`) + const peerInfo = this.libp2pService?.getCurrentPeerInfo() + await this.libp2pService?.redialPeers([...peerInfo.connected, ...peerInfo.dialed]) + }) this.socketService.on(SocketActionTypes.CONNECTION_PROCESS_INFO, data => { this.serverIoProvider.io.emit(SocketActionTypes.CONNECTION_PROCESS_INFO, data) }) @@ -604,9 +663,9 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI // Update Frontend with Initialized Communities if (this.communityId) { this.serverIoProvider.io.emit(SocketActionTypes.COMMUNITY_LAUNCHED, { id: this.communityId }) - console.log('this.libp2pService.dialedPeers', this.libp2pService.dialedPeers) - console.log('this.libp2pService.connectedPeers', this.libp2pService.connectedPeers) - console.log('this.libp2pservice', this.libp2pService) + this.logger('this.libp2pService.connectedPeers', this.libp2pService.connectedPeers) + this.logger('this.libp2pservice', this.libp2pService) + this.logger('this.libp2pService.dialedPeers', this.libp2pService.dialedPeers) this.serverIoProvider.io.emit( SocketActionTypes.CONNECTED_PEERS, Array.from(this.libp2pService.connectedPeers.keys()) @@ -639,8 +698,10 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI callback(await this.joinCommunity(args)) } ) - this.socketService.on(SocketActionTypes.LEAVE_COMMUNITY, async () => { - await this.leaveCommunity() + + this.socketService.on(SocketActionTypes.LEAVE_COMMUNITY, async (callback: (closed: boolean) => void) => { + this.logger(`socketService - ${SocketActionTypes.LEAVE_COMMUNITY}`) + callback(await this.leaveCommunity()) }) // Username registration diff --git a/packages/backend/src/nest/libp2p/libp2p.service.ts b/packages/backend/src/nest/libp2p/libp2p.service.ts index 00a3763f8a..dcec245cd4 100644 --- a/packages/backend/src/nest/libp2p/libp2p.service.ts +++ b/packages/backend/src/nest/libp2p/libp2p.service.ts @@ -22,6 +22,7 @@ import { webSockets } from '../websocketOverTor' import { all } from '../websocketOverTor/filters' import { Libp2pConnectedPeer, Libp2pEvents, Libp2pNodeParams, Libp2pPeerInfo } from './libp2p.types' import { ProcessInChunksService } from './process-in-chunks.service' +import { peerIdFromString } from '@libp2p/peer-id' const KEY_LENGTH = 32 export const LIBP2P_PSK_METADATA = '/key/swarm/psk/1.0.0/\n/base16/\n' @@ -42,6 +43,7 @@ export class Libp2pService extends EventEmitter { private dialPeer = async (peerAddress: string) => { if (this.dialedPeers.has(peerAddress)) { + this.logger(`Skipping dial of ${peerAddress} because its already been dialed`) return } this.dialedPeers.add(peerAddress) @@ -94,13 +96,27 @@ export class Libp2pService extends EventEmitter { for (const peer of peers) { await this.hangUpPeer(peer) } + this.logger('All peers hung up') } public async hangUpPeer(peerAddress: string) { this.logger('Hanging up on peer', peerAddress) - await this.libp2pInstance?.hangUp(multiaddr(peerAddress)) + try { + const ma = multiaddr(peerAddress) + const peerId = peerIdFromString(ma.getPeerId()!) + + this.logger('Hanging up connection on libp2p') + await this.libp2pInstance?.hangUp(ma) + + this.logger('Removing peer from peer store') + await this.libp2pInstance?.peerStore.delete(peerId as any) + } catch (e) { + this.logger.error(e) + } + this.logger('Clearing local data') this.dialedPeers.delete(peerAddress) this.connectedPeers.delete(peerAddress) + this.logger('Done hanging up') } /** @@ -108,11 +124,9 @@ export class Libp2pService extends EventEmitter { * iOS where Tor receives a new port when the app resumes from background and * we want to close/re-open connections. */ - public async redialPeers(peerInfo?: Libp2pPeerInfo) { - const dialed = peerInfo ? peerInfo.dialed : Array.from(this.dialedPeers) - const toDial = peerInfo - ? [...peerInfo.connected, ...peerInfo.dialed] - : [...this.connectedPeers.keys(), ...this.dialedPeers] + public async redialPeers(peersToDial?: string[]) { + const dialed = peersToDial ?? Array.from(this.dialedPeers) + const toDial = peersToDial ?? [...this.connectedPeers.keys(), ...this.dialedPeers] if (dialed.length === 0) { this.logger('No peers to redial!') @@ -122,9 +136,7 @@ export class Libp2pService extends EventEmitter { this.logger(`Re-dialing ${dialed.length} peers`) // TODO: Sort peers - for (const peerAddress of dialed) { - await this.hangUpPeer(peerAddress) - } + await this.hangUpPeers(dialed) this.processInChunksService.updateData(toDial) await this.processInChunksService.process() @@ -142,7 +154,7 @@ export class Libp2pService extends EventEmitter { start: false, connectionManager: { minConnections: 3, // TODO: increase? - maxConnections: 8, // TODO: increase? + maxConnections: 20, // TODO: increase? dialTimeout: 120_000, maxParallelDials: 10, autoDial: true, // It's a default but let's set it to have explicit information diff --git a/packages/backend/src/nest/libp2p/process-in-chunks.service.ts b/packages/backend/src/nest/libp2p/process-in-chunks.service.ts index e57e681227..bad92b873d 100644 --- a/packages/backend/src/nest/libp2p/process-in-chunks.service.ts +++ b/packages/backend/src/nest/libp2p/process-in-chunks.service.ts @@ -71,7 +71,7 @@ export class ProcessInChunksService extends EventEmitter { } public async process() { - this.logger(`Processing ${this.taskQueue.length} items`) + this.logger(`Processing ${this.taskQueue.length()} items`) this.taskQueue.resume() } diff --git a/packages/backend/src/nest/socket/socket.service.ts b/packages/backend/src/nest/socket/socket.service.ts index 10b9797491..e6f4b17c00 100644 --- a/packages/backend/src/nest/socket/socket.service.ts +++ b/packages/backend/src/nest/socket/socket.service.ts @@ -173,9 +173,9 @@ export class SocketService extends EventEmitter implements OnModuleInit { } ) - socket.on(SocketActionTypes.LEAVE_COMMUNITY, async () => { + socket.on(SocketActionTypes.LEAVE_COMMUNITY, async (callback: (closed: boolean) => void) => { this.logger('Leaving community') - this.emit(SocketActionTypes.LEAVE_COMMUNITY) + this.emit(SocketActionTypes.LEAVE_COMMUNITY, callback) }) socket.on(SocketActionTypes.LIBP2P_PSK_STORED, payload => { diff --git a/packages/backend/src/nest/storage/certificates/certificates.store.ts b/packages/backend/src/nest/storage/certificates/certificates.store.ts index 6341d51d0e..c262e96a07 100644 --- a/packages/backend/src/nest/storage/certificates/certificates.store.ts +++ b/packages/backend/src/nest/storage/certificates/certificates.store.ts @@ -41,6 +41,7 @@ export class CertificatesStore extends EventEmitter { write: ['*'], }, }) + await this.store.load() this.store.events.on('ready', async () => { this.logger('Loaded certificates to memory') @@ -58,8 +59,8 @@ export class CertificatesStore extends EventEmitter { await this.loadedCertificates() }) - // @ts-expect-error - OrbitDB's type declaration of `load` lacks 'options' - await this.store.load({ fetchEntryTimeout: 15000 }) + // // @ts-expect-error - OrbitDB's type declaration of `load` lacks 'options' + // await this.store.load({ fetchEntryTimeout: 15000 }) this.logger('Initialized') } @@ -147,7 +148,9 @@ export class CertificatesStore extends EventEmitter { * https://github.com/TryQuiet/quiet/issues/1899 */ public async getCertificates(): Promise { + this.logger('Getting certificates') if (!this.store) { + this.logger('No store found!') return [] } diff --git a/packages/backend/src/nest/tor/tor-control.service.ts b/packages/backend/src/nest/tor/tor-control.service.ts index b83040c7a4..9051994853 100644 --- a/packages/backend/src/nest/tor/tor-control.service.ts +++ b/packages/backend/src/nest/tor/tor-control.service.ts @@ -65,8 +65,7 @@ export class TorControl { this.logger('Tor connected') return } catch (e) { - this.logger(e) - this.logger('Retrying...') + this.logger.error('Retrying due to error...', e) await new Promise(r => setTimeout(r, 500)) } } @@ -76,7 +75,7 @@ export class TorControl { try { this.connection?.end() } catch (e) { - this.logger.error('Disconnect failed:', e.message) + this.logger.error('Disconnect failed:', e) } this.connection = null } @@ -94,6 +93,7 @@ export class TorControl { resolve({ code: 250, messages: dataArray }) } else { clearTimeout(connectionTimeout) + console.error(`TOR CONNECTION ERROR: ${JSON.stringify(dataArray, null, 2)}`) reject(`${dataArray[0]}`) } clearTimeout(connectionTimeout) @@ -104,6 +104,7 @@ export class TorControl { } public async sendCommand(command: string): Promise<{ code: number; messages: string[] }> { + this.logger(`Sending tor command: ${command}`) // Only send one command at a time. if (this.isSending) { this.logger('Tor connection already established, waiting...') @@ -111,7 +112,9 @@ export class TorControl { // Wait for existing command to finish. while (this.isSending) { - await new Promise(r => setTimeout(r, 750)) + const timeout = 750 + this.logger(`Waiting for ${timeout}ms to retry command...`) + await new Promise(r => setTimeout(r, timeout)) } this.isSending = true diff --git a/packages/backend/src/nest/tor/tor.service.ts b/packages/backend/src/nest/tor/tor.service.ts index 030925eb9c..6b739f0572 100644 --- a/packages/backend/src/nest/tor/tor.service.ts +++ b/packages/backend/src/nest/tor/tor.service.ts @@ -21,7 +21,7 @@ export class Tor extends EventEmitter implements OnModuleInit { extraTorProcessParams: TorParams controlPort: number | undefined interval: any - timeout: any + initTimeout: any private readonly logger = Logger(Tor.name) private hiddenServices: Map = new Map() private initializedHiddenServices: Map = new Map() @@ -59,12 +59,23 @@ export class Tor extends EventEmitter implements OnModuleInit { return Array.from(Object.entries(this.extraTorProcessParams)).flat() } + private async isBootstrappingFinished(): Promise { + this.logger('Checking bootstrap status') + const output = await this.torControl.sendCommand('GETINFO status/bootstrap-phase') + if (output.messages[0] === '250-status/bootstrap-phase=NOTICE BOOTSTRAP PROGRESS=100 TAG=done SUMMARY="Done"') { + this.logger('Bootstrapping finished!') + return true + } + return false + } + public async init(timeout = 120_000): Promise { if (!this.socksPort) this.socksPort = await getPort() this.logger('Initializing tor...') return await new Promise((resolve, reject) => { if (!fs.existsSync(this.quietDir)) { + this.logger("Quiet dir doesn't exist, creating it now") fs.mkdirSync(this.quietDir) } @@ -77,9 +88,10 @@ export class Tor extends EventEmitter implements OnModuleInit { this.logger(`${this.torPidPath} exists. Old tor pid: ${oldTorPid}`) } - this.timeout = setTimeout(async () => { - const log = await this.torControl.sendCommand('GETINFO status/bootstrap-phase') - if (log.messages[0] !== '250-status/bootstrap-phase=NOTICE BOOTSTRAP PROGRESS=100 TAG=done SUMMARY="Done"') { + this.initTimeout = setTimeout(async () => { + this.logger('Checking init timeout') + const bootstrapDone = await this.isBootstrappingFinished() + if (!bootstrapDone) { this.initializedHiddenServices = new Map() clearInterval(this.interval) await this.init() @@ -104,12 +116,14 @@ export class Tor extends EventEmitter implements OnModuleInit { await this.spawnTor() this.interval = setInterval(async () => { - const log = await this.torControl.sendCommand('GETINFO status/bootstrap-phase') - if ( - log.messages[0] === '250-status/bootstrap-phase=NOTICE BOOTSTRAP PROGRESS=100 TAG=done SUMMARY="Done"' - ) { + this.logger('Checking bootstrap interval') + const bootstrapDone = await this.isBootstrappingFinished() + if (bootstrapDone) { + this.logger(`Sending ${SocketActionTypes.TOR_INITIALIZED}`) this.serverIoProvider.io.emit(SocketActionTypes.TOR_INITIALIZED) - + // TODO: Figure out how to get redialing (or, ideally, initial dialing) on tor initialization working + // this.logger('Attempting to redial peers (if possible)') + // this.emit(SocketActionTypes.REDIAL_PEERS) clearInterval(this.interval) } }, 2500) @@ -265,8 +279,10 @@ export class Tor extends EventEmitter implements OnModuleInit { this.process.stdout.on('data', (data: any) => { this.logger(data.toString()) - const regexp = /Bootstrapped 0/ - if (regexp.test(data.toString())) { + const bootstrappedRegexp = /Bootstrapped 0/ + // TODO: Figure out if there's a way to get this working in tests + // const bootstrappedRegexp = /Loaded enough directory info to build circuits/ + if (bootstrappedRegexp.test(data.toString())) { this.spawnHiddenServices() resolve() } @@ -279,6 +295,7 @@ export class Tor extends EventEmitter implements OnModuleInit { } public async spawnHiddenServices() { + this.logger(`Spawning hidden service(s) (count: ${this.hiddenServices.size})`) for (const el of this.hiddenServices.values()) { await this.spawnHiddenService(el) } @@ -293,6 +310,7 @@ export class Tor extends EventEmitter implements OnModuleInit { privKey: string virtPort?: number }): Promise { + this.logger(`Spawning Tor hidden service`) const initializedHiddenService = this.initializedHiddenServices.get(privKey) if (initializedHiddenService) { this.logger(`Hidden service already initialized for ${initializedHiddenService.onionAddress}`) @@ -302,6 +320,7 @@ export class Tor extends EventEmitter implements OnModuleInit { `ADD_ONION ${privKey} Flags=Detach Port=${virtPort},127.0.0.1:${targetPort}` ) const onionAddress = status.messages[0].replace('250-ServiceID=', '') + this.logger(`Spawned hidden service with onion address ${onionAddress}`) const hiddenService: HiddenServiceData = { targetPort, privKey, virtPort, onionAddress } this.hiddenServices.set(privKey, hiddenService) @@ -369,7 +388,7 @@ export class Tor extends EventEmitter implements OnModuleInit { resolve() return } - if (this.timeout) clearTimeout(this.timeout) + if (this.initTimeout) clearTimeout(this.initTimeout) if (this.interval) clearInterval(this.interval) this.process?.on('close', () => { this.process = null diff --git a/packages/backend/src/nest/websocketOverTor/index.ts b/packages/backend/src/nest/websocketOverTor/index.ts index e714970e8b..c0b1160214 100644 --- a/packages/backend/src/nest/websocketOverTor/index.ts +++ b/packages/backend/src/nest/websocketOverTor/index.ts @@ -84,14 +84,14 @@ export class WebSockets extends EventEmitter { signal: options.signal, }) } catch (e) { - log.error('error connecting to %s. Details: %s', ma, e.message) + log.error('error connecting to %s. Details: %s', ma, e) throw e } try { maConn = socketToMaConn(socket, ma, { signal: options.signal }) log('new outbound connection %s', maConn.remoteAddr) } catch (e) { - log.error('error creating new outbound connection %s. Details: %s', ma, e.message) + log.error('error creating new outbound connection %s. Details: %s', ma, e) throw e } @@ -100,7 +100,7 @@ export class WebSockets extends EventEmitter { log('outbound connection %s upgraded', maConn.remoteAddr) return conn } catch (e) { - log.error('error upgrading outbound connection %s. Details: %s', maConn.remoteAddr, e.message) + log.error('error upgrading outbound connection %s. Details: %s', maConn.remoteAddr, e) throw e } } @@ -114,7 +114,7 @@ export class WebSockets extends EventEmitter { const errorPromise = pDefer() const errfn = (event: ErrorEvent) => { - log.error(`connection error: ${event.message}`) + log.error(`connection error`, event) errorPromise.reject(event) } @@ -229,7 +229,10 @@ export class WebSockets extends EventEmitter { listener.emit('connection', conn) }) .on('listening', () => listener.emit('listening')) - .on('error', err => listener.emit('error', err)) + .on('error', err => { + log.error(`Websocket error`, err) + listener.emit('error', err) + }) .on('close', () => listener.emit('close')) // Keep track of open connections to destroy in case of timeout diff --git a/packages/mobile/ios/Quiet/Info.plist b/packages/mobile/ios/Quiet/Info.plist index b628587bef..da427de23a 100644 --- a/packages/mobile/ios/Quiet/Info.plist +++ b/packages/mobile/ios/Quiet/Info.plist @@ -36,26 +36,26 @@ CFBundleVersion 372 ITSAppUsesNonExemptEncryption - + LSRequiresIPhoneOS - + NSAppTransportSecurity NSAllowsArbitraryLoads - + NSAllowsLocalNetworking - + NSExceptionDomains localhost NSExceptionAllowsInsecureHTTPLoads - + NSLocationWhenInUseUsageDescription - + UIAppFonts Rubik-Black.ttf @@ -74,7 +74,7 @@ Rubik-SemiBoldItalic.ttf UIBackgroundModes - + UILaunchStoryboardName LaunchScreen UIRequiredDeviceCapabilities @@ -88,6 +88,6 @@ UIInterfaceOrientationLandscapeRight UIViewControllerBasedStatusBarAppearance - + diff --git a/packages/mobile/src/components/Input/Input.styles.ts b/packages/mobile/src/components/Input/Input.styles.ts index 08ad3f9b62..8adaeea25d 100644 --- a/packages/mobile/src/components/Input/Input.styles.ts +++ b/packages/mobile/src/components/Input/Input.styles.ts @@ -8,7 +8,7 @@ export const StyledTextInput = styled(TextInput)<{ }>` ${({ height, multiline }) => css` text-align-vertical: center; - height: ${Math.max(40, height)}; + height: ${Math.max(40, height)}px; ${Platform.select({ ios: { paddingTop: 12, diff --git a/packages/mobile/src/store/init/startConnection/startConnection.saga.ts b/packages/mobile/src/store/init/startConnection/startConnection.saga.ts index 4cc55f53f9..44a48cfbc6 100644 --- a/packages/mobile/src/store/init/startConnection/startConnection.saga.ts +++ b/packages/mobile/src/store/init/startConnection/startConnection.saga.ts @@ -1,5 +1,5 @@ import { io } from 'socket.io-client' -import { select, put, call, cancel, fork, takeEvery, FixedTask, delay, apply } from 'typed-redux-saga' +import { select, put, call, cancel, fork, takeEvery, FixedTask, delay, apply, putResolve } from 'typed-redux-saga' import { PayloadAction } from '@reduxjs/toolkit' import { socket as stateManager, Socket } from '@quiet/state-manager' import { encodeSecret } from '@quiet/common' @@ -91,5 +91,5 @@ function subscribeSocketLifecycle(socket: Socket, socketIOData: WebsocketConnect function* cancelRootTaskSaga(task: FixedTask): Generator { console.warn('Canceling root task', task.error()) yield* cancel(task) - yield* put(initActions.canceledRootTask()) + yield* putResolve(initActions.canceledRootTask()) } diff --git a/packages/mobile/src/store/nativeServices/leaveCommunity/leaveCommunity.saga.ts b/packages/mobile/src/store/nativeServices/leaveCommunity/leaveCommunity.saga.ts index 33df360946..56ec0c77f7 100644 --- a/packages/mobile/src/store/nativeServices/leaveCommunity/leaveCommunity.saga.ts +++ b/packages/mobile/src/store/nativeServices/leaveCommunity/leaveCommunity.saga.ts @@ -1,4 +1,4 @@ -import { select, call, put, takeLeading } from 'typed-redux-saga' +import { select, call, takeLeading, putResolve } from 'typed-redux-saga' import { app } from '@quiet/state-manager' import { persistor } from '../../store' import { nativeServicesActions } from '../nativeServices.slice' @@ -11,7 +11,7 @@ export function* leaveCommunitySaga(): Generator { console.log('Leaving community') // Restart backend - yield* put(app.actions.closeServices()) + yield* putResolve(app.actions.closeServices()) yield takeLeading(initActions.canceledRootTask.type, clearReduxStore) } @@ -23,18 +23,25 @@ export function* clearReduxStore(): Generator { console.info('Clearing redux store') // Stop persistor + console.info('Pausing persistor') yield* call(persistor.pause) + console.info('Flushing persistor') yield* call(persistor.flush) + console.info('Purging persistor') yield* call(persistor.purge) // Clear redux store - yield* put(nativeServicesActions.resetApp()) + console.info('Resetting app') + yield* putResolve(nativeServicesActions.resetApp()) // Resume persistor + console.info('Resuming persistor') yield* call(persistor.persist) // Restarting persistor doesn't mark store as ready automatically - yield* put(initActions.setStoreReady()) + console.info('Set store ready') + yield* putResolve(initActions.setStoreReady()) - yield* put(navigationActions.replaceScreen({ screen: ScreenNames.JoinCommunityScreen })) + console.info('Opening join community screen') + yield* putResolve(navigationActions.replaceScreen({ screen: ScreenNames.JoinCommunityScreen })) } diff --git a/packages/state-manager/src/sagas/app/closeServices.saga.ts b/packages/state-manager/src/sagas/app/closeServices.saga.ts index aed87f384d..0557a247b9 100644 --- a/packages/state-manager/src/sagas/app/closeServices.saga.ts +++ b/packages/state-manager/src/sagas/app/closeServices.saga.ts @@ -8,5 +8,5 @@ export function* closeServicesSaga( socket: Socket, _action: PayloadAction['payload']> ): Generator { - yield* apply(socket, socket.emit, [SocketActionTypes.LEAVE_COMMUNITY]) + yield* apply(socket, socket.emitWithAck, [SocketActionTypes.LEAVE_COMMUNITY]) } diff --git a/packages/state-manager/src/sagas/communities/createCommunity/createCommunity.saga.ts b/packages/state-manager/src/sagas/communities/createCommunity/createCommunity.saga.ts index fcbb78c499..8308e764f1 100644 --- a/packages/state-manager/src/sagas/communities/createCommunity/createCommunity.saga.ts +++ b/packages/state-manager/src/sagas/communities/createCommunity/createCommunity.saga.ts @@ -1,5 +1,5 @@ import { type Socket, applyEmitParams } from '../../../types' -import { select, apply, put } from 'typed-redux-saga' +import { select, apply, putResolve } from 'typed-redux-saga' import { type PayloadAction } from '@reduxjs/toolkit' import { identityActions } from '../../identity/identity.slice' import { communitiesSelectors } from '../communities.selectors' @@ -49,21 +49,21 @@ export function* createCommunitySaga( return } - yield* put(communitiesActions.updateCommunityData(createdCommunity)) + yield* putResolve(communitiesActions.updateCommunityData(createdCommunity)) - yield* put( + yield* putResolve( identityActions.storeUserCertificate({ communityId: createdCommunity.id, userCertificate: createdCommunity.ownerCertificate, }) ) - yield* put(publicChannelsActions.createGeneralChannel()) + yield* putResolve(publicChannelsActions.createGeneralChannel()) // TODO: We can likely refactor this a bit. Currently, we issue the owner's // certificate before creating the community, but then we add the owner's CSR // to the OrbitDB store after creating the community (in the following saga). // We can likely add the owner's CSR when creating the community or decouple // community creation from CSR/certificate creation and create the community // first and then add the owner's CSR and issue their certificate. - yield* put(identityActions.saveUserCsr()) + yield* putResolve(identityActions.saveUserCsr()) } diff --git a/packages/state-manager/src/sagas/publicChannels/channelsReplicated/channelsReplicated.saga.test.ts b/packages/state-manager/src/sagas/publicChannels/channelsReplicated/channelsReplicated.saga.test.ts index a28df7e920..548d6600e0 100644 --- a/packages/state-manager/src/sagas/publicChannels/channelsReplicated/channelsReplicated.saga.test.ts +++ b/packages/state-manager/src/sagas/publicChannels/channelsReplicated/channelsReplicated.saga.test.ts @@ -87,7 +87,7 @@ describe('channelsReplicatedSaga', () => { ) .withReducer(reducer) .withState(store.getState()) - .put( + .putResolve( publicChannelsActions.addChannel({ channel: sailingChannel, }) @@ -108,12 +108,12 @@ describe('channelsReplicatedSaga', () => { ) .withReducer(reducer) .withState(store.getState()) - .not.put( + .not.putResolve( publicChannelsActions.addChannel({ channel: generalChannel, }) ) - .put( + .putResolve( publicChannelsActions.addChannel({ channel: sailingChannel, }) @@ -134,12 +134,12 @@ describe('channelsReplicatedSaga', () => { ) .withReducer(reducer) .withState(store.getState()) - .put( + .putResolve( publicChannelsActions.addChannel({ channel: sailingChannel, }) ) - .put( + .putResolve( messagesActions.addPublicChannelsMessagesBase({ channelId: sailingChannel.id, }) @@ -160,22 +160,22 @@ describe('channelsReplicatedSaga', () => { ) .withReducer(reducer) .withState(store.getState()) - .put( + .putResolve( publicChannelsActions.addChannel({ channel: sailingChannel, }) ) - .put( + .putResolve( messagesActions.addPublicChannelsMessagesBase({ channelId: sailingChannel.id, }) ) - .not.put( + .not.putResolve( publicChannelsActions.addChannel({ channel: generalChannel, }) ) - .not.put( + .not.putResolve( messagesActions.addPublicChannelsMessagesBase({ channelId: generalChannel.id, }) @@ -208,7 +208,7 @@ describe('channelsReplicatedSaga', () => { ) .withReducer(reducer) .withState(store.getState()) - .put(messages.actions.resetCurrentPublicChannelCache()) + .putResolve(messages.actions.resetCurrentPublicChannelCache()) .run() }) @@ -230,7 +230,7 @@ describe('channelsReplicatedSaga', () => { ) .withReducer(reducer) .withState(store.getState()) - .not.put(messages.actions.resetCurrentPublicChannelCache()) + .not.putResolve(messages.actions.resetCurrentPublicChannelCache()) .run() }) @@ -249,9 +249,9 @@ describe('channelsReplicatedSaga', () => { ) .withReducer(reducer) .withState(store.getState()) - .put(publicChannelsActions.deleteChannel({ channelId: photoChannel.id })) + .putResolve(publicChannelsActions.deleteChannel({ channelId: photoChannel.id })) .dispatch(publicChannelsActions.completeChannelDeletion({})) - .put( + .putResolve( publicChannelsActions.addChannel({ channel: sailingChannel, }) @@ -269,7 +269,7 @@ describe('channelsReplicatedSaga', () => { ) .withReducer(reducer) .withState(store.getState()) - .not.put(publicChannelsActions.deleteChannel({ channelId: generalChannel.id })) + .not.putResolve(publicChannelsActions.deleteChannel({ channelId: generalChannel.id })) .run() }) }) diff --git a/packages/state-manager/src/sagas/publicChannels/channelsReplicated/channelsReplicated.saga.ts b/packages/state-manager/src/sagas/publicChannels/channelsReplicated/channelsReplicated.saga.ts index ebf15f8f16..7bdd971b07 100644 --- a/packages/state-manager/src/sagas/publicChannels/channelsReplicated/channelsReplicated.saga.ts +++ b/packages/state-manager/src/sagas/publicChannels/channelsReplicated/channelsReplicated.saga.ts @@ -1,5 +1,5 @@ import { type PayloadAction } from '@reduxjs/toolkit' -import { select, put, take } from 'typed-redux-saga' +import { select, put, take, putResolve } from 'typed-redux-saga' import { publicChannelsSelectors } from '../publicChannels.selectors' import { publicChannelsActions } from '../publicChannels.slice' import { messagesSelectors } from '../../messages/messages.selectors' @@ -30,12 +30,12 @@ export function* channelsReplicatedSaga( if (!locallyStoredChannels.includes(channel.id)) { // TODO: Refactor to use QuietLogger log(`Adding #${channel.name} to store`) - yield* put( + yield* putResolve( publicChannelsActions.addChannel({ channel, }) ) - yield* put( + yield* putResolve( messagesActions.addPublicChannelsMessagesBase({ channelId: channel.id, }) @@ -49,7 +49,7 @@ export function* channelsReplicatedSaga( if (!databaseStoredChannelsIds.includes(channelId)) { // TODO: Refactor to use QuietLogger log(`Removing #${channelId} from store`) - yield* put(publicChannelsActions.deleteChannel({ channelId })) + yield* putResolve(publicChannelsActions.deleteChannel({ channelId })) yield* take(publicChannelsActions.completeChannelDeletion) } } @@ -60,12 +60,12 @@ export function* channelsReplicatedSaga( // (On collecting data from persist) Populating displayable data if (currentChannelCache.length < 1 && currentChannelRepository.length > 0) { - yield* put(messagesActions.resetCurrentPublicChannelCache()) + yield* putResolve(messagesActions.resetCurrentPublicChannelCache()) } const community = yield* select(communitiesSelectors.currentCommunity) if (!community?.CA && databaseStoredChannels.find(channel => channel.name === 'general')) { - yield* put(publicChannelsActions.sendIntroductionMessage()) + yield* putResolve(publicChannelsActions.sendIntroductionMessage()) } } diff --git a/packages/types/src/socket.ts b/packages/types/src/socket.ts index c74ba5dd14..4fcb44cfdb 100644 --- a/packages/types/src/socket.ts +++ b/packages/types/src/socket.ts @@ -71,6 +71,7 @@ export enum SocketActionTypes { PEER_CONNECTED = 'peerConnected', PEER_DISCONNECTED = 'peerDisconnected', TOR_INITIALIZED = 'torInitialized', + REDIAL_PEERS = 'redialPeers', // ====== Misc ====== From dde5d344ebabae6badb77c7554ceba501904cc38 Mon Sep 17 00:00:00 2001 From: Isla Koenigsknecht Date: Tue, 7 May 2024 15:14:21 -0400 Subject: [PATCH 26/48] Update required contextual actions to run and exit early when no relevant changes found (#2488) --- .github/workflows/backend-tests.yml | 50 ++++++++++++++++++- .../check-desktop-visual-regression.yml | 18 ++++++- .github/workflows/desktop-rtl-tests.yml | 20 ++++++-- .github/workflows/desktop-test-scroll.yml | 22 ++++++-- .github/workflows/desktop-tests.yml | 18 ++++++- .github/workflows/identity-tests.yml | 18 ++++++- .github/workflows/state-manager-tests.yml | 18 ++++++- CHANGELOG.md | 1 + 8 files changed, 149 insertions(+), 16 deletions(-) diff --git a/.github/workflows/backend-tests.yml b/.github/workflows/backend-tests.yml index 8235f50d28..cfbb49e884 100644 --- a/.github/workflows/backend-tests.yml +++ b/.github/workflows/backend-tests.yml @@ -2,8 +2,6 @@ name: Backend unit tests on: pull_request: - paths: - - packages/backend/** jobs: unit-tests: @@ -14,17 +12,33 @@ jobs: os: [ubuntu-20.04, macos-latest, windows-2019] steps: + - uses: dorny/paths-filter@v3 + id: filter + with: + filters: | + backend: + - 'packages/backend/**' + - name: "Skip tests" + if: steps.filter.outputs.backend == 'false' + run: | + echo "Skipping test run" + exit 0 + - name: "Print OS" + if: steps.filter.outputs.backend == 'true' run: echo ${{ matrix.os }} - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + if: steps.filter.outputs.backend == 'true' - name: "Setup environment" + if: steps.filter.outputs.backend == 'true' uses: ./.github/actions/setup-env with: bootstrap-packages: "@quiet/eslint-config,@quiet/logger,@quiet/common,@quiet/types,@quiet/state-manager,@quiet/backend,@quiet/identity,backend-bundle" - name: "Unit tests" + if: steps.filter.outputs.backend == 'true' run: lerna run test-ci --scope @quiet/backend --stream long-running-tests: @@ -35,17 +49,33 @@ jobs: os: [ubuntu-20.04] steps: + - uses: dorny/paths-filter@v3 + id: filter + with: + filters: | + backend: + - 'packages/backend/**' + - name: "Skip tests" + if: steps.filter.outputs.backend == 'false' + run: | + echo "Skipping test run" + exit 0 + - name: "Print OS" + if: steps.filter.outputs.backend == 'true' run: echo ${{ matrix.os }} - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + if: steps.filter.outputs.backend == 'true' - name: "Setup environment" + if: steps.filter.outputs.backend == 'true' uses: ./.github/actions/setup-env with: bootstrap-packages: "@quiet/eslint-config,@quiet/logger,@quiet/common,@quiet/types,@quiet/state-manager,@quiet/backend,@quiet/identity,backend-bundle" - name: "Unit tests" + if: steps.filter.outputs.backend == 'true' run: lerna run test-ci-long-running --scope @quiet/backend --stream unit-tests-with-tor: @@ -56,15 +86,31 @@ jobs: os: [ubuntu-20.04] steps: + - uses: dorny/paths-filter@v3 + id: filter + with: + filters: | + backend: + - 'packages/backend/**' + - name: "Skip tests" + if: steps.filter.outputs.backend == 'false' + run: | + echo "Skipping test run" + exit 0 + - name: "Print OS" + if: steps.filter.outputs.backend == 'true' run: echo ${{ matrix.os }} - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + if: steps.filter.outputs.backend == 'true' - name: "Setup environment" + if: steps.filter.outputs.backend == 'true' uses: ./.github/actions/setup-env with: bootstrap-packages: "@quiet/eslint-config,@quiet/logger,@quiet/common,@quiet/types,@quiet/state-manager,@quiet/backend,@quiet/identity,backend-bundle" - name: "Unit tests" + if: steps.filter.outputs.backend == 'true' run: lerna run test-ci-tor --scope @quiet/backend --stream diff --git a/.github/workflows/check-desktop-visual-regression.yml b/.github/workflows/check-desktop-visual-regression.yml index 3b8599420c..7ecc4123e0 100644 --- a/.github/workflows/check-desktop-visual-regression.yml +++ b/.github/workflows/check-desktop-visual-regression.yml @@ -2,8 +2,6 @@ name: Desktop visual regressions on: pull_request: - paths: - - packages/desktop/** jobs: chromatic-deployment: @@ -14,20 +12,36 @@ jobs: os: [ubuntu-20.04] steps: + - uses: dorny/paths-filter@v3 + id: filter + with: + filters: | + desktop: + - 'packages/desktop/**' + - name: "Skip tests" + if: steps.filter.outputs.desktop == 'false' + run: | + echo "Skipping test run" + exit 0 + - name: "Print OS" + if: steps.filter.outputs.desktop == 'true' run: echo ${{ matrix.os }} - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + if: steps.filter.outputs.desktop == 'true' with: fetch-depth: 0 # Required to retrieve git history - name: Setup environment uses: ./.github/actions/setup-env + if: steps.filter.outputs.desktop == 'true' with: bootstrap-packages: "@quiet/eslint-config,@quiet/logger,@quiet/common,@quiet/types,@quiet/state-manager,@quiet/backend,@quiet/identity,@quiet/desktop,backend-bundle" - name: "Publish to Chromatic" uses: chromaui/action@355e2a05a179e9e89c2b237dcd55adbeb89e577e # v1 + if: steps.filter.outputs.desktop == 'true' with: workingDir: ./packages/desktop token: ${{ secrets.GH_TOKEN }} diff --git a/.github/workflows/desktop-rtl-tests.yml b/.github/workflows/desktop-rtl-tests.yml index 8e8343b736..0ceb2042a4 100644 --- a/.github/workflows/desktop-rtl-tests.yml +++ b/.github/workflows/desktop-rtl-tests.yml @@ -2,9 +2,6 @@ name: Desktop - state-manager bracket tests (RTL) on: pull_request: - paths: - - packages/desktop/** - - packages/state-manager/** jobs: desktop-tests: @@ -16,16 +13,33 @@ jobs: os: [ubuntu-20.04, macos-latest] steps: + - uses: dorny/paths-filter@v3 + id: filter + with: + filters: | + validFiles: + - 'packages/desktop/**' + - 'packages/state-manger/**' + - name: "Skip tests" + if: steps.filter.outputs.validFiles == 'false' + run: | + echo "Skipping test run" + exit 0 + - name: "Print OS" + if: steps.filter.outputs.validFiles == 'true' run: echo ${{ matrix.os }} - uses: actions/checkout@v3 + if: steps.filter.outputs.validFiles == 'true' - name: "Setup environment" uses: ./.github/actions/setup-env + if: steps.filter.outputs.validFiles == 'true' with: cachePrefix: "desktop-tests" bootstrap-packages: "@quiet/eslint-config,@quiet/logger,@quiet/common,@quiet/types,@quiet/state-manager,@quiet/backend,@quiet/identity,@quiet/desktop,backend-bundle" - name: "Desktop - state-manager bracket tests" + if: steps.filter.outputs.validFiles == 'true' run: lerna run rtl-test --scope @quiet/desktop --stream diff --git a/.github/workflows/desktop-test-scroll.yml b/.github/workflows/desktop-test-scroll.yml index 0ee999eb78..9f1c0b9ee0 100644 --- a/.github/workflows/desktop-test-scroll.yml +++ b/.github/workflows/desktop-test-scroll.yml @@ -2,8 +2,6 @@ name: Desktop scroll regression tests on: pull_request: - paths: - - packages/desktop/** jobs: regression-tests: @@ -15,31 +13,49 @@ jobs: os: [ubuntu-20.04] steps: + - uses: dorny/paths-filter@v3 + id: filter + with: + filters: | + desktop: + - 'packages/desktop/**' + - name: "Skip tests" + if: steps.filter.outputs.desktop == 'false' + run: | + echo "Skipping test run" + exit 0 + - name: "Print OS" + if: steps.filter.outputs.desktop == 'true' run: echo ${{ matrix.os }} - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + if: steps.filter.outputs.desktop == 'true' - name: Setup environment uses: ./.github/actions/setup-env + if: steps.filter.outputs.desktop == 'true' with: bootstrap-packages: "@quiet/eslint-config,@quiet/logger,@quiet/common,@quiet/types,@quiet/state-manager,@quiet/backend,@quiet/identity,@quiet/desktop,backend-bundle" - name: "Install libs" + if: steps.filter.outputs.desktop == 'true' run: sudo apt-get update && sudo apt-get install -y libgtk2.0-0 libgtk-3-0 libnotify-dev libgconf-2-4 libnss3 libxss1 libasound2 libxtst6 xauth xvfb - name: "Remove test files workaround" + if: steps.filter.outputs.desktop == 'true' run: find packages/desktop/src -name '*.test.*' -delete && find packages/backend/src -name '*.test.*' -delete - uses: cypress-io/github-action@1b70233146622b69e789ccdd4f9452adc638d25a # v6.6.1 + if: steps.filter.outputs.desktop == 'true' with: install: false command: npm run regression-test:ci working-directory: packages/desktop - name: Archive test screenshots + if: steps.filter.outputs.desktop == 'true' uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 - if: always() with: name: test-screenshots-linux path: packages/desktop/cypress/snapshots diff --git a/.github/workflows/desktop-tests.yml b/.github/workflows/desktop-tests.yml index 38fbf3592f..5af62b1a81 100644 --- a/.github/workflows/desktop-tests.yml +++ b/.github/workflows/desktop-tests.yml @@ -2,8 +2,6 @@ name: Desktop tests on: pull_request: - paths: - - packages/desktop/** jobs: desktop-tests: @@ -15,16 +13,32 @@ jobs: os: [ubuntu-20.04, macos-latest] steps: + - uses: dorny/paths-filter@v3 + id: filter + with: + filters: | + desktop: + - 'packages/desktop/**' + - name: "Skip tests" + if: steps.filter.outputs.desktop == 'false' + run: | + echo "Skipping test run" + exit 0 + - name: "Print OS" + if: steps.filter.outputs.desktop == 'true' run: echo ${{ matrix.os }} - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + if: steps.filter.outputs.desktop == 'true' - name: "Setup environment" uses: ./.github/actions/setup-env + if: steps.filter.outputs.desktop == 'true' with: cachePrefix: "desktop-tests" bootstrap-packages: "@quiet/eslint-config,@quiet/logger,@quiet/common,@quiet/types,@quiet/state-manager,@quiet/backend,@quiet/identity,@quiet/desktop,backend-bundle" - name: "Unit tests" + if: steps.filter.outputs.desktop == 'true' run: lerna run test --scope @quiet/desktop --stream diff --git a/.github/workflows/identity-tests.yml b/.github/workflows/identity-tests.yml index ea66be2b38..2c156c1c43 100644 --- a/.github/workflows/identity-tests.yml +++ b/.github/workflows/identity-tests.yml @@ -2,8 +2,6 @@ name: Identity tests on: pull_request: - paths: - - packages/identity/** jobs: identity-tests: @@ -15,15 +13,31 @@ jobs: os: [ubuntu-20.04, macos-latest, windows-2019] steps: + - uses: dorny/paths-filter@v3 + id: filter + with: + filters: | + identity: + - 'packages/identity/**' + - name: "Skip tests" + if: steps.filter.outputs.identity == 'false' + run: | + echo "Skipping test run" + exit 0 + - name: "Print OS" + if: steps.filter.outputs.identity == 'true' run: echo ${{ matrix.os }} - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + if: steps.filter.outputs.identity == 'true' - name: "Setup environment" uses: ./.github/actions/setup-env + if: steps.filter.outputs.identity == 'true' with: bootstrap-packages: "@quiet/eslint-config,@quiet/logger,@quiet/types,@quiet/identity,backend-bundle" - name: "Unit tests" + if: steps.filter.outputs.identity == 'true' run: lerna run test --scope @quiet/identity --stream diff --git a/.github/workflows/state-manager-tests.yml b/.github/workflows/state-manager-tests.yml index cd68e78a5b..c7fa0bf0bd 100644 --- a/.github/workflows/state-manager-tests.yml +++ b/.github/workflows/state-manager-tests.yml @@ -2,8 +2,6 @@ name: State-manager tests on: pull_request: - paths: - - packages/state-manager/** jobs: state-manager-tests: @@ -15,15 +13,31 @@ jobs: os: [ubuntu-20.04, macos-latest] steps: + - uses: dorny/paths-filter@v3 + id: filter + with: + filters: | + stateManager: + - 'packages/state-manager/**' + - name: "Skip tests" + if: steps.filter.outputs.stateManager == 'false' + run: | + echo "Skipping test run" + exit 0 + - name: "Print OS" + if: steps.filter.outputs.stateManager == 'true' run: echo ${{ matrix.os }} - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + if: steps.filter.outputs.stateManager == 'true' - name: "Setup environment" uses: ./.github/actions/setup-env + if: steps.filter.outputs.stateManager == 'true' with: bootstrap-packages: "@quiet/eslint-config,@quiet/logger,@quiet/common,@quiet/types,@quiet/state-manager,@quiet/backend,@quiet/identity,@quiet/desktop,backend-bundle" - name: "Unit tests" + if: steps.filter.outputs.stateManager == 'true' run: lerna run test --scope @quiet/state-manager --stream diff --git a/CHANGELOG.md b/CHANGELOG.md index 3512ab6d81..dbfe219b55 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ # Fixes: +* Update github workflows for PR gating ([#2487](https://github.com/TryQuiet/quiet/issues/2487)) * Don't create duplicate CSRs when joining a community under certain circumstances ([#2321](https://github.com/TryQuiet/quiet/issues/2321)) [2.2.0] From 0495c3883c7014edbf11dfd7cc4d3423657bcba8 Mon Sep 17 00:00:00 2001 From: Lucas Leblow Date: Wed, 8 May 2024 15:10:11 -0600 Subject: [PATCH 27/48] Publish - @quiet/desktop@2.2.0-alpha.4 - @quiet/mobile@2.2.0-alpha.4 --- packages/desktop/CHANGELOG.md | 13 +++++++++++++ packages/desktop/package-lock.json | 4 ++-- packages/desktop/package.json | 2 +- packages/mobile/CHANGELOG.md | 16 ++++++++++++++++ packages/mobile/android/app/build.gradle | 4 ++-- packages/mobile/ios/Quiet/Info.plist | 18 +++++++++--------- packages/mobile/ios/QuietTests/Info.plist | 2 +- packages/mobile/package-lock.json | 4 ++-- packages/mobile/package.json | 2 +- 9 files changed, 47 insertions(+), 18 deletions(-) diff --git a/packages/desktop/CHANGELOG.md b/packages/desktop/CHANGELOG.md index dcc0e8492c..29d22cb8d0 100644 --- a/packages/desktop/CHANGELOG.md +++ b/packages/desktop/CHANGELOG.md @@ -1,3 +1,16 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +# [2.2.0-alpha.4](https://github.com/TryQuiet/quiet/compare/@quiet/desktop@2.2.0-alpha.3...@quiet/desktop@2.2.0-alpha.4) (2024-05-08) + +**Note:** Version bump only for package @quiet/desktop + + + + + [unreleased] # New features: diff --git a/packages/desktop/package-lock.json b/packages/desktop/package-lock.json index 94aef27a84..c7404ce111 100644 --- a/packages/desktop/package-lock.json +++ b/packages/desktop/package-lock.json @@ -1,12 +1,12 @@ { "name": "@quiet/desktop", - "version": "2.2.0-alpha.3", + "version": "2.2.0-alpha.4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@quiet/desktop", - "version": "2.2.0-alpha.3", + "version": "2.2.0-alpha.4", "license": "GPL-3.0-or-later", "dependencies": { "@electron/remote": "^2.0.8", diff --git a/packages/desktop/package.json b/packages/desktop/package.json index 31f8fd265d..7b648586a6 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -80,7 +80,7 @@ }, "homepage": "https://github.com/TryQuiet", "@comment version": "To build new version for specific platform, just replace platform in version tag to one of following linux, mac, windows", - "version": "2.2.0-alpha.3", + "version": "2.2.0-alpha.4", "description": "Decentralized team chat", "main": "dist/main/main.js", "scripts": { diff --git a/packages/mobile/CHANGELOG.md b/packages/mobile/CHANGELOG.md index 3512ab6d81..8c906dcf7b 100644 --- a/packages/mobile/CHANGELOG.md +++ b/packages/mobile/CHANGELOG.md @@ -1,3 +1,19 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +# [2.2.0-alpha.4](https://github.com/TryQuiet/quiet/compare/@quiet/mobile@2.2.0-alpha.3...@quiet/mobile@2.2.0-alpha.4) (2024-05-08) + + +### Bug Fixes + +* start websocket connection on react init ([#2481](https://github.com/TryQuiet/quiet/issues/2481)) ([d3b822b](https://github.com/TryQuiet/quiet/commit/d3b822b795e0bd2e79ca0104897b63ef93701f9a)) + + + + + [unreleased] # New features: diff --git a/packages/mobile/android/app/build.gradle b/packages/mobile/android/app/build.gradle index ab1c47b248..47445b66e8 100644 --- a/packages/mobile/android/app/build.gradle +++ b/packages/mobile/android/app/build.gradle @@ -192,8 +192,8 @@ android { applicationId "com.quietmobile" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 415 - versionName "2.2.0-alpha.3" + versionCode 416 + versionName "2.2.0-alpha.4" resValue "string", "build_config_package", "com.quietmobile" testBuildType System.getProperty('testBuildType', 'debug') testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' diff --git a/packages/mobile/ios/Quiet/Info.plist b/packages/mobile/ios/Quiet/Info.plist index da427de23a..54ba75c093 100644 --- a/packages/mobile/ios/Quiet/Info.plist +++ b/packages/mobile/ios/Quiet/Info.plist @@ -34,28 +34,28 @@ CFBundleVersion - 372 + 373 ITSAppUsesNonExemptEncryption - + LSRequiresIPhoneOS - + NSAppTransportSecurity NSAllowsArbitraryLoads - + NSAllowsLocalNetworking - + NSExceptionDomains localhost NSExceptionAllowsInsecureHTTPLoads - + NSLocationWhenInUseUsageDescription - + UIAppFonts Rubik-Black.ttf @@ -74,7 +74,7 @@ Rubik-SemiBoldItalic.ttf UIBackgroundModes - + UILaunchStoryboardName LaunchScreen UIRequiredDeviceCapabilities @@ -88,6 +88,6 @@ UIInterfaceOrientationLandscapeRight UIViewControllerBasedStatusBarAppearance - + diff --git a/packages/mobile/ios/QuietTests/Info.plist b/packages/mobile/ios/QuietTests/Info.plist index 9932ae800d..536de9c787 100644 --- a/packages/mobile/ios/QuietTests/Info.plist +++ b/packages/mobile/ios/QuietTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 372 + 373 diff --git a/packages/mobile/package-lock.json b/packages/mobile/package-lock.json index 440ba5b932..3ef4481a0c 100644 --- a/packages/mobile/package-lock.json +++ b/packages/mobile/package-lock.json @@ -1,12 +1,12 @@ { "name": "@quiet/mobile", - "version": "2.2.0-alpha.3", + "version": "2.2.0-alpha.4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@quiet/mobile", - "version": "2.2.0-alpha.3", + "version": "2.2.0-alpha.4", "dependencies": { "@peculiar/webcrypto": "^1.4.3", "@react-native-clipboard/clipboard": "^1.13.2", diff --git a/packages/mobile/package.json b/packages/mobile/package.json index c64bb7a325..5f8603da74 100644 --- a/packages/mobile/package.json +++ b/packages/mobile/package.json @@ -1,6 +1,6 @@ { "name": "@quiet/mobile", - "version": "2.2.0-alpha.3", + "version": "2.2.0-alpha.4", "scripts": { "build": "tsc -p tsconfig.build.json --noEmit", "storybook-android": "ENVFILE=.env.storybook react-native run-android --mode=storybookDebug --appIdSuffix=storybook.debug", From e5c7559f85c28d8dfab5acba35a63cfb3a28d718 Mon Sep 17 00:00:00 2001 From: Lucas Leblow Date: Wed, 8 May 2024 15:10:14 -0600 Subject: [PATCH 28/48] Update packages CHANGELOG.md --- packages/desktop/CHANGELOG.md | 16 ++-------------- packages/mobile/CHANGELOG.md | 17 +---------------- 2 files changed, 3 insertions(+), 30 deletions(-) diff --git a/packages/desktop/CHANGELOG.md b/packages/desktop/CHANGELOG.md index 29d22cb8d0..dbfe219b55 100644 --- a/packages/desktop/CHANGELOG.md +++ b/packages/desktop/CHANGELOG.md @@ -1,16 +1,3 @@ -# Change Log - -All notable changes to this project will be documented in this file. -See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - -# [2.2.0-alpha.4](https://github.com/TryQuiet/quiet/compare/@quiet/desktop@2.2.0-alpha.3...@quiet/desktop@2.2.0-alpha.4) (2024-05-08) - -**Note:** Version bump only for package @quiet/desktop - - - - - [unreleased] # New features: @@ -19,6 +6,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline # Fixes: +* Update github workflows for PR gating ([#2487](https://github.com/TryQuiet/quiet/issues/2487)) * Don't create duplicate CSRs when joining a community under certain circumstances ([#2321](https://github.com/TryQuiet/quiet/issues/2321)) [2.2.0] @@ -34,9 +22,9 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline # Fixes: -* Allow JPEG and GIF files as profile photos ([#2332](https://github.com/TryQuiet/quiet/issues/2332)) * Fix issues with recreating general channel when deleted while offline ([#2334](https://github.com/TryQuiet/quiet/issues/2334)) * Fix package.json license inconsistency +* Fixes issue with reconnecting to peers on resume on iOS ([#2424](https://github.com/TryQuiet/quiet/issues/2424)) [2.1.2] diff --git a/packages/mobile/CHANGELOG.md b/packages/mobile/CHANGELOG.md index 8c906dcf7b..dbfe219b55 100644 --- a/packages/mobile/CHANGELOG.md +++ b/packages/mobile/CHANGELOG.md @@ -1,19 +1,3 @@ -# Change Log - -All notable changes to this project will be documented in this file. -See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - -# [2.2.0-alpha.4](https://github.com/TryQuiet/quiet/compare/@quiet/mobile@2.2.0-alpha.3...@quiet/mobile@2.2.0-alpha.4) (2024-05-08) - - -### Bug Fixes - -* start websocket connection on react init ([#2481](https://github.com/TryQuiet/quiet/issues/2481)) ([d3b822b](https://github.com/TryQuiet/quiet/commit/d3b822b795e0bd2e79ca0104897b63ef93701f9a)) - - - - - [unreleased] # New features: @@ -22,6 +6,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline # Fixes: +* Update github workflows for PR gating ([#2487](https://github.com/TryQuiet/quiet/issues/2487)) * Don't create duplicate CSRs when joining a community under certain circumstances ([#2321](https://github.com/TryQuiet/quiet/issues/2321)) [2.2.0] From 6225d9795dcd9b9365fe42d105430c537b6fa32e Mon Sep 17 00:00:00 2001 From: Isla Koenigsknecht Date: Thu, 9 May 2024 09:05:04 -0400 Subject: [PATCH 29/48] Create utils-tests.yml --- .github/workflows/utils-tests.yml | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 .github/workflows/utils-tests.yml diff --git a/.github/workflows/utils-tests.yml b/.github/workflows/utils-tests.yml new file mode 100644 index 0000000000..b002c21ee3 --- /dev/null +++ b/.github/workflows/utils-tests.yml @@ -0,0 +1,29 @@ +name: Common package tests + +on: + pull_request: + paths: + - packages/common/** + +jobs: + utils-tests: + timeout-minutes: 25 + runs-on: ${{ matrix.os }} + + strategy: + matrix: + os: [ubuntu-20.04, macos-12, windows-2019] + + steps: + - name: "Print OS" + run: echo ${{ matrix.os }} + + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + + - name: "Setup environment" + uses: ./.github/actions/setup-env + with: + bootstrap-packages: "@quiet/eslint-config,@quiet/logger,@quiet/types,@quiet/common" + + - name: "Unit tests" + run: lerna run test --scope @quiet/common --stream From bcbc20a8fe166718918d00b02d20b053a01164a5 Mon Sep 17 00:00:00 2001 From: Isla Koenigsknecht Date: Thu, 9 May 2024 09:05:37 -0400 Subject: [PATCH 30/48] Revert "Create utils-tests.yml" This reverts commit 6225d9795dcd9b9365fe42d105430c537b6fa32e. --- .github/workflows/utils-tests.yml | 29 ----------------------------- 1 file changed, 29 deletions(-) delete mode 100644 .github/workflows/utils-tests.yml diff --git a/.github/workflows/utils-tests.yml b/.github/workflows/utils-tests.yml deleted file mode 100644 index b002c21ee3..0000000000 --- a/.github/workflows/utils-tests.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: Common package tests - -on: - pull_request: - paths: - - packages/common/** - -jobs: - utils-tests: - timeout-minutes: 25 - runs-on: ${{ matrix.os }} - - strategy: - matrix: - os: [ubuntu-20.04, macos-12, windows-2019] - - steps: - - name: "Print OS" - run: echo ${{ matrix.os }} - - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - - - name: "Setup environment" - uses: ./.github/actions/setup-env - with: - bootstrap-packages: "@quiet/eslint-config,@quiet/logger,@quiet/types,@quiet/common" - - - name: "Unit tests" - run: lerna run test --scope @quiet/common --stream From c51d29a6bee6f893fd499348f3782995a47322ba Mon Sep 17 00:00:00 2001 From: Isla Koenigsknecht Date: Thu, 9 May 2024 09:40:08 -0400 Subject: [PATCH 31/48] Chore: Use macos-12 on 2.2.0 (#2515) * macos-latest -> macos-12 (#2482) * fix: use explicit macos-12 image instead of macos-latest ('-latest' image was updated to macos-14) * Update e2e-mac.yml * Run e2e on e2e changes --------- Co-authored-by: breadhunter <6099829+EmiM@users.noreply.github.com> --- .github/workflows/backend-tests.yml | 2 +- .github/workflows/desktop-build.yml | 2 +- .github/workflows/desktop-rtl-tests.yml | 2 +- .github/workflows/desktop-tests.yml | 2 +- .github/workflows/e2e-android.yml | 4 ++-- .github/workflows/e2e-crossplatform.yml | 1 + .github/workflows/e2e-mac.yml | 6 ++--- .github/workflows/identity-tests.yml | 2 +- .github/workflows/mobile-deploy-ios.yml | 2 +- .github/workflows/state-manager-tests.yml | 2 +- .github/workflows/utils-tests.yml | 29 +++++++++++++++++++++++ 11 files changed, 42 insertions(+), 12 deletions(-) create mode 100644 .github/workflows/utils-tests.yml diff --git a/.github/workflows/backend-tests.yml b/.github/workflows/backend-tests.yml index cfbb49e884..3d6aacfa32 100644 --- a/.github/workflows/backend-tests.yml +++ b/.github/workflows/backend-tests.yml @@ -9,7 +9,7 @@ jobs: strategy: matrix: - os: [ubuntu-20.04, macos-latest, windows-2019] + os: [ubuntu-20.04, macos-12, windows-2019] steps: - uses: dorny/paths-filter@v3 diff --git a/.github/workflows/desktop-build.yml b/.github/workflows/desktop-build.yml index 7781362d15..68a52349d5 100644 --- a/.github/workflows/desktop-build.yml +++ b/.github/workflows/desktop-build.yml @@ -96,7 +96,7 @@ jobs: build-macos: # needs: run-e2e-tests-mac - runs-on: macos-latest + runs-on: macos-12 if: | startsWith(github.ref, 'refs/tags/@quiet/desktop') diff --git a/.github/workflows/desktop-rtl-tests.yml b/.github/workflows/desktop-rtl-tests.yml index 0ceb2042a4..02232b9c95 100644 --- a/.github/workflows/desktop-rtl-tests.yml +++ b/.github/workflows/desktop-rtl-tests.yml @@ -10,7 +10,7 @@ jobs: strategy: matrix: - os: [ubuntu-20.04, macos-latest] + os: [ubuntu-20.04, macos-12] steps: - uses: dorny/paths-filter@v3 diff --git a/.github/workflows/desktop-tests.yml b/.github/workflows/desktop-tests.yml index 5af62b1a81..9acb5f1ec8 100644 --- a/.github/workflows/desktop-tests.yml +++ b/.github/workflows/desktop-tests.yml @@ -10,7 +10,7 @@ jobs: strategy: matrix: - os: [ubuntu-20.04, macos-latest] + os: [ubuntu-20.04, macos-12] steps: - uses: dorny/paths-filter@v3 diff --git a/.github/workflows/e2e-android.yml b/.github/workflows/e2e-android.yml index 60ad8b4502..e5819bd58a 100644 --- a/.github/workflows/e2e-android.yml +++ b/.github/workflows/e2e-android.yml @@ -10,8 +10,8 @@ on: jobs: detox-android: - timeout-minutes: 10 - runs-on: [self-hosted, macOS, ARM64, android] + timeout-minutes: 25 + runs-on: [macos-14-xlarge] steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/e2e-crossplatform.yml b/.github/workflows/e2e-crossplatform.yml index b45d5caddb..5d34f17a0c 100644 --- a/.github/workflows/e2e-crossplatform.yml +++ b/.github/workflows/e2e-crossplatform.yml @@ -8,6 +8,7 @@ on: - packages/state-manager/** - packages/identity/** - packages/common/** + - .github/workflows/e2e** jobs: mac: diff --git a/.github/workflows/e2e-mac.yml b/.github/workflows/e2e-mac.yml index 880e627678..9a636124cc 100644 --- a/.github/workflows/e2e-mac.yml +++ b/.github/workflows/e2e-mac.yml @@ -3,7 +3,7 @@ name: E2E Mac on: [workflow_call] jobs: mac: - runs-on: macos-latest + runs-on: macos-12 timeout-minutes: 180 env: ELECTRON_CUSTOM_VERSION: 23.0.0 @@ -34,7 +34,7 @@ jobs: - name: FILE_NAME env working-directory: ./packages/desktop/dist - run: echo "FILE_NAME="Quiet-$VERSION-arm64.dmg"" >> $GITHUB_ENV + run: echo "FILE_NAME="Quiet-$VERSION.dmg"" >> $GITHUB_ENV - name: Chmod working-directory: ./packages/desktop/dist @@ -45,7 +45,7 @@ jobs: run: hdiutil mount $FILE_NAME - name: Add App file to applications - run: cd ~ && cp -R "/Volumes/Quiet $VERSION-arm64/Quiet.app" /Applications + run: cd ~ && cp -R "/Volumes/Quiet $VERSION/Quiet.app" /Applications - name: Run invitation link test - Includes 2 separate application clients uses: nick-fields/retry@14672906e672a08bd6eeb15720e9ed3ce869cdd4 # v2.9.0 diff --git a/.github/workflows/identity-tests.yml b/.github/workflows/identity-tests.yml index 2c156c1c43..e2562a47a3 100644 --- a/.github/workflows/identity-tests.yml +++ b/.github/workflows/identity-tests.yml @@ -10,7 +10,7 @@ jobs: strategy: matrix: - os: [ubuntu-20.04, macos-latest, windows-2019] + os: [ubuntu-20.04, macos-12, windows-2019] steps: - uses: dorny/paths-filter@v3 diff --git a/.github/workflows/mobile-deploy-ios.yml b/.github/workflows/mobile-deploy-ios.yml index 6560298c51..657cc5df20 100644 --- a/.github/workflows/mobile-deploy-ios.yml +++ b/.github/workflows/mobile-deploy-ios.yml @@ -14,7 +14,7 @@ jobs: strategy: matrix: - os: [macOS-latest] + os: [macos-12] steps: - name: "Print OS" diff --git a/.github/workflows/state-manager-tests.yml b/.github/workflows/state-manager-tests.yml index c7fa0bf0bd..51f204c276 100644 --- a/.github/workflows/state-manager-tests.yml +++ b/.github/workflows/state-manager-tests.yml @@ -10,7 +10,7 @@ jobs: strategy: matrix: - os: [ubuntu-20.04, macos-latest] + os: [ubuntu-20.04, macos-12] steps: - uses: dorny/paths-filter@v3 diff --git a/.github/workflows/utils-tests.yml b/.github/workflows/utils-tests.yml new file mode 100644 index 0000000000..b002c21ee3 --- /dev/null +++ b/.github/workflows/utils-tests.yml @@ -0,0 +1,29 @@ +name: Common package tests + +on: + pull_request: + paths: + - packages/common/** + +jobs: + utils-tests: + timeout-minutes: 25 + runs-on: ${{ matrix.os }} + + strategy: + matrix: + os: [ubuntu-20.04, macos-12, windows-2019] + + steps: + - name: "Print OS" + run: echo ${{ matrix.os }} + + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + + - name: "Setup environment" + uses: ./.github/actions/setup-env + with: + bootstrap-packages: "@quiet/eslint-config,@quiet/logger,@quiet/types,@quiet/common" + + - name: "Unit tests" + run: lerna run test --scope @quiet/common --stream From 134fbcb3249ffb4743e0fcfb30b08be355440566 Mon Sep 17 00:00:00 2001 From: Lucas Leblow Date: Thu, 9 May 2024 17:17:31 -0600 Subject: [PATCH 32/48] fix: Reorder the closing of services, prevent sagas running multiple times and close backend server properly (#2499) --- CHANGELOG.md | 1 + .../backend/ipfs-pubsub-peer-monitor.patch | 11 +++ packages/backend/package.json | 2 +- packages/backend/src/backendManager.ts | 8 +- .../connections-manager.service.ts | 39 ++++----- .../backend/src/nest/libp2p/libp2p.module.ts | 2 - .../nest/registration/registration.service.ts | 2 +- .../backend/src/nest/socket/socket.service.ts | 81 ++++++++++++++++--- .../certificatesRequestsStore.ts | 9 +-- .../certificates/certificates.store.ts | 8 +- .../communityMetadata.store.ts | 8 +- .../src/nest/storage/storage.service.ts | 26 ++---- .../storage/userProfile/userProfile.store.ts | 5 +- packages/mobile/src/store/init/init.slice.ts | 3 - .../init/setupCrypto/setupCrypto.saga.test.ts | 27 ------- .../init/setupCrypto/setupCrypto.saga.ts | 24 ------ .../startConnection/startConnection.saga.ts | 37 +++++---- .../events/nativeServicesCallbacks.ts | 18 +++-- .../leaveCommunity/leaveCommunity.saga.ts | 5 +- .../nativeServices.master.saga.ts | 20 +++-- packages/mobile/src/store/root.saga.ts | 63 ++++++++++++--- packages/mobile/src/store/store.ts | 1 + .../src/sagas/app/app.master.saga.ts | 20 +++-- .../appConnection/connection.master.saga.ts | 12 ++- .../communities/communities.master.saga.ts | 22 +++-- .../createCommunity/createCommunity.saga.ts | 2 + .../createNetwork/createNetwork.saga.ts | 9 ++- .../src/sagas/errors/errors.master.saga.ts | 12 ++- .../src/sagas/files/files.master.saga.ts | 32 +++++--- .../sagas/identity/identity.master.saga.ts | 22 +++-- .../registerUsername/registerUsername.saga.ts | 8 +- .../identity/saveUserCsr/saveUserCsr.saga.ts | 12 ++- .../sagas/messages/messages.master.saga.ts | 39 +++++---- .../messages/sendMessage/sendMessage.saga.ts | 1 + .../publicChannels.master.saga.ts | 30 ++++--- .../startConnection/startConnection.saga.ts | 50 ++++++++---- .../src/sagas/users/users.master.saga.ts | 12 ++- 37 files changed, 418 insertions(+), 265 deletions(-) create mode 100644 packages/backend/ipfs-pubsub-peer-monitor.patch delete mode 100644 packages/mobile/src/store/init/setupCrypto/setupCrypto.saga.test.ts delete mode 100644 packages/mobile/src/store/init/setupCrypto/setupCrypto.saga.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index dbfe219b55..36baaed49a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ * Fix issues with recreating general channel when deleted while offline ([#2334](https://github.com/TryQuiet/quiet/issues/2334)) * Fix package.json license inconsistency * Fixes issue with reconnecting to peers on resume on iOS ([#2424](https://github.com/TryQuiet/quiet/issues/2424)) +* Reorder the closing of services, prevent sagas running multiple times and close backend server properly [2.1.2] diff --git a/packages/backend/ipfs-pubsub-peer-monitor.patch b/packages/backend/ipfs-pubsub-peer-monitor.patch new file mode 100644 index 0000000000..3ee35dc77d --- /dev/null +++ b/packages/backend/ipfs-pubsub-peer-monitor.patch @@ -0,0 +1,11 @@ +--- node_modules/ipfs-pubsub-peer-monitor/src/ipfs-pubsub-peer-monitor.js 2024-05-08 12:44:48 ++++ node_modules/ipfs-pubsub-peer-monitor/src/ipfs-pubsub-peer-monitor.backup.js 2024-05-08 12:44:25 +@@ -55,7 +55,7 @@ + async _pollPeers () { + try { + const peers = await this._pubsub.peers(this._topic) +- IpfsPubsubPeerMonitor._emitJoinsAndLeaves(new Set(this._peers), new Set(peers), this) ++ IpfsPubsubPeerMonitor._emitJoinsAndLeaves(new Set(this._peers.map(p => p.toString())), new Set(peers.map(p => p.toString())), this) + this._peers = peers + } catch (err) { + clearInterval(this._interval) \ No newline at end of file diff --git a/packages/backend/package.json b/packages/backend/package.json index 87312a5a6e..e663a57080 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -12,7 +12,7 @@ "build": "tsc -p tsconfig.build.json", "webpack": "webpack --env mode=development && cp ./lib/bundle.cjs ../backend-bundle/bundle.cjs", "webpack:prod": "webpack --env mode=production && cp ./lib/bundle.cjs ../backend-bundle/bundle.cjs", - "applyPatches": "patch -f -p0 < ./electron-fetch.patch || true && patch -f -p0 --forward --binary < ./parse-duration.patch || true && patch -f -p0 --forward --binary < ./parse-duration-esm.patch || true", + "applyPatches": "patch -f -p0 < ./electron-fetch.patch || true && patch -f -p0 --forward --binary < ./parse-duration.patch || true && patch -f -p0 --forward --binary < ./parse-duration-esm.patch || true && patch -f -p0 < ./ipfs-pubsub-peer-monitor.patch || true", "prepare": "npm run applyPatches && npm run webpack", "version": "git add -A src", "lint:no-fix": "eslint --ext .jsx,.js,.ts,.tsx ./src/", diff --git a/packages/backend/src/backendManager.ts b/packages/backend/src/backendManager.ts index 63fd7449be..d76d655a9b 100644 --- a/packages/backend/src/backendManager.ts +++ b/packages/backend/src/backendManager.ts @@ -109,12 +109,12 @@ export const runBackendMobile = async () => { { logger: ['warn', 'error', 'log', 'debug', 'verbose'] } ) - rn_bridge.channel.on('close', async () => { + rn_bridge.channel.on('close', () => { const connectionsManager = app.get(ConnectionsManagerService) - await connectionsManager.pause() + connectionsManager.pause() }) - rn_bridge.channel.on('open', async (msg: OpenServices) => { + rn_bridge.channel.on('open', (msg: OpenServices) => { const connectionsManager = app.get(ConnectionsManagerService) const torControl = app.get(TorControl) const proxyAgent = app.get<{ proxy: { port: string } }>(SOCKS_PROXY_AGENT) @@ -123,7 +123,7 @@ export const runBackendMobile = async () => { torControl.torControlParams.auth.value = msg.authCookie proxyAgent.proxy.port = msg.httpTunnelPort - await connectionsManager.resume() + connectionsManager.resume() }) } diff --git a/packages/backend/src/nest/connections-manager/connections-manager.service.ts b/packages/backend/src/nest/connections-manager/connections-manager.service.ts index 586bc941a4..a030206be7 100644 --- a/packages/backend/src/nest/connections-manager/connections-manager.service.ts +++ b/packages/backend/src/nest/connections-manager/connections-manager.service.ts @@ -224,6 +224,10 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI } public async closeAllServices(options: { saveTor: boolean } = { saveTor: false }) { + this.logger('Closing services') + + await this.closeSocket() + if (this.tor && !options.saveTor) { this.logger('Killing tor') await this.tor.kill() @@ -231,31 +235,26 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI this.logger('Saving tor') } if (this.storageService) { - this.logger('Stopping orbitdb') + this.logger('Stopping OrbitDB') await this.storageService?.stopOrbitDb() } - if (this.serverIoProvider?.io) { - this.logger('Closing socket server') - this.serverIoProvider.io.close() - } - if (this.localDbService) { - this.logger('Closing local storage') - await this.localDbService.close() - } if (this.libp2pService) { this.logger('Stopping libp2p') await this.libp2pService.close() } + if (this.localDbService) { + this.logger('Closing local DB') + await this.localDbService.close() + } } - public closeSocket() { - this.serverIoProvider.io.close() + public async closeSocket() { + await this.socketService.close() } public async pause() { this.logger('Pausing!') - this.logger('Closing socket!') - this.closeSocket() + await this.closeSocket() this.logger('Pausing libp2pService!') this.peerInfo = await this.libp2pService?.pause() this.logger('Found the following peer info on pause: ', this.peerInfo) @@ -263,7 +262,6 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI public async resume() { this.logger('Resuming!') - this.logger('Reopening socket!') await this.openSocket() this.logger('Attempting to redial peers!') if (this.peerInfo && (this.peerInfo?.connected.length !== 0 || this.peerInfo?.dialed.length !== 0)) { @@ -289,21 +287,14 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI public async leaveCommunity(): Promise { this.logger('Running leaveCommunity') - this.logger('Resetting tor') - this.tor.resetHiddenServices() - - this.logger('Closing the socket') - this.closeSocket() - - this.logger('Purging local DB') - await this.localDbService.purge() - - this.logger('Closing services') await this.closeAllServices({ saveTor: true }) this.logger('Purging data') await this.purgeData() + this.logger('Resetting Tor') + this.tor.resetHiddenServices() + this.logger('Resetting state') await this.resetState() diff --git a/packages/backend/src/nest/libp2p/libp2p.module.ts b/packages/backend/src/nest/libp2p/libp2p.module.ts index d75edc799e..e010bb8b55 100644 --- a/packages/backend/src/nest/libp2p/libp2p.module.ts +++ b/packages/backend/src/nest/libp2p/libp2p.module.ts @@ -1,10 +1,8 @@ import { Module } from '@nestjs/common' -import { SocketModule } from '../socket/socket.module' import { Libp2pService } from './libp2p.service' import { ProcessInChunksService } from './process-in-chunks.service' @Module({ - imports: [SocketModule], providers: [Libp2pService, ProcessInChunksService], exports: [Libp2pService], }) diff --git a/packages/backend/src/nest/registration/registration.service.ts b/packages/backend/src/nest/registration/registration.service.ts index 17cd8ff838..5e6057c91b 100644 --- a/packages/backend/src/nest/registration/registration.service.ts +++ b/packages/backend/src/nest/registration/registration.service.ts @@ -51,7 +51,7 @@ export class RegistrationService extends EventEmitter implements OnModuleInit { // Get the next event. const event = this.registrationEvents.shift() if (event) { - this.logger('Processing registration event', event) + this.logger('Processing registration event') // Event processing in progress this.registrationEventInProgress = true diff --git a/packages/backend/src/nest/socket/socket.service.ts b/packages/backend/src/nest/socket/socket.service.ts index e6f4b17c00..9d34f37dd6 100644 --- a/packages/backend/src/nest/socket/socket.service.ts +++ b/packages/backend/src/nest/socket/socket.service.ts @@ -27,6 +27,7 @@ import { CONFIG_OPTIONS, SERVER_IO_PROVIDER } from '../const' import { ConfigOptions, ServerIoProviderTypes } from '../types' import { suspendableSocketEvents } from './suspendable.events' import Logger from '../common/logger' +import type net from 'node:net' @Injectable() export class SocketService extends EventEmitter implements OnModuleInit { @@ -34,6 +35,7 @@ export class SocketService extends EventEmitter implements OnModuleInit { public resolveReadyness: (value: void | PromiseLike) => void public readyness: Promise + private sockets: Set constructor( @Inject(SERVER_IO_PROVIDER) public readonly serverIoProvider: ServerIoProviderTypes, @@ -44,14 +46,15 @@ export class SocketService extends EventEmitter implements OnModuleInit { this.readyness = new Promise(resolve => { this.resolveReadyness = resolve }) + + this.sockets = new Set() + + this.attachListeners() } async onModuleInit() { this.logger('init: Started') - - this.attachListeners() await this.init() - this.logger('init: Finished') } @@ -71,7 +74,9 @@ export class SocketService extends EventEmitter implements OnModuleInit { this.logger('init: Frontend connected') } - private readonly attachListeners = (): void => { + private readonly attachListeners = () => { + this.logger('Attaching listeners') + // Attach listeners here this.serverIoProvider.io.on(SocketActionTypes.CONNECTION, socket => { this.logger('Socket connection') @@ -173,7 +178,7 @@ export class SocketService extends EventEmitter implements OnModuleInit { } ) - socket.on(SocketActionTypes.LEAVE_COMMUNITY, async (callback: (closed: boolean) => void) => { + socket.on(SocketActionTypes.LEAVE_COMMUNITY, (callback: (closed: boolean) => void) => { this.logger('Leaving community') this.emit(SocketActionTypes.LEAVE_COMMUNITY, callback) }) @@ -195,11 +200,53 @@ export class SocketService extends EventEmitter implements OnModuleInit { this.emit(SocketActionTypes.LOAD_MIGRATION_DATA, data) }) }) + + // Ensure the underlying connections get closed. See: + // https://github.com/socketio/socket.io/issues/1602 + this.serverIoProvider.server.on('connection', conn => { + this.sockets.add(conn) + conn.on('close', () => { + this.sockets.delete(conn) + }) + }) } - public listen = async (port = this.configOptions.socketIOPort): Promise => { - return await new Promise(resolve => { - if (this.serverIoProvider.server.listening) resolve() + public getConnections = (): Promise => { + return new Promise(resolve => { + this.serverIoProvider.server.getConnections((err, count) => { + if (err) throw new Error(err.message) + resolve(count) + }) + }) + } + + // Ensure the underlying connections get closed. See: + // https://github.com/socketio/socket.io/issues/1602 + // + // I also tried `this.serverIoProvider.io.disconnectSockets(true)` + // which didn't work for me, but we still call it. + public closeSockets = () => { + this.logger('Disconnecting sockets') + this.serverIoProvider.io.disconnectSockets(true) + this.sockets.forEach(s => s.destroy()) + } + + public listen = async (): Promise => { + this.logger(`Opening data server on port ${this.configOptions.socketIOPort}`) + + if (this.serverIoProvider.server.listening) { + this.logger('Failed to listen. Server already listening.') + return + } + + const numConnections = await this.getConnections() + + if (numConnections > 0) { + this.logger('Failed to listen. Connections still open:', numConnections) + return + } + + return new Promise(resolve => { this.serverIoProvider.server.listen(this.configOptions.socketIOPort, '127.0.0.1', () => { this.logger(`Data server running on port ${this.configOptions.socketIOPort}`) resolve() @@ -207,13 +254,23 @@ export class SocketService extends EventEmitter implements OnModuleInit { }) } - public close = async (): Promise => { - this.logger(`Closing data server on port ${this.configOptions.socketIOPort}`) - return await new Promise(resolve => { - this.serverIoProvider.server.close(err => { + public close = (): Promise => { + return new Promise(resolve => { + this.logger(`Closing data server on port ${this.configOptions.socketIOPort}`) + + if (!this.serverIoProvider.server.listening) { + this.logger('Data server is not running.') + resolve() + return + } + + this.serverIoProvider.io.close(err => { if (err) throw new Error(err.message) + this.logger('Data server closed') resolve() }) + + this.closeSockets() }) } } diff --git a/packages/backend/src/nest/storage/certifacteRequests/certificatesRequestsStore.ts b/packages/backend/src/nest/storage/certifacteRequests/certificatesRequestsStore.ts index be2d6c54b5..0b23a48cf0 100644 --- a/packages/backend/src/nest/storage/certifacteRequests/certificatesRequestsStore.ts +++ b/packages/backend/src/nest/storage/certifacteRequests/certificatesRequestsStore.ts @@ -29,6 +29,7 @@ export class CertificatesRequestsStore extends EventEmitter { write: ['*'], }, }) + await this.store.load() this.store.events.on('write', async (_address, entry) => { this.logger('Added CSR to database') @@ -40,8 +41,6 @@ export class CertificatesRequestsStore extends EventEmitter { this.loadedCertificateRequests() }) - // @ts-ignore - await this.store.load({ fetchEntryTimeout: 15000 }) this.logger('Initialized') } @@ -52,9 +51,9 @@ export class CertificatesRequestsStore extends EventEmitter { } public async close() { - this.logger('Closing...') + this.logger('Closing certificate requests DB') await this.store?.close() - this.logger('Closed') + this.logger('Closed certificate requests DB') } public getAddress() { @@ -91,8 +90,6 @@ export class CertificatesRequestsStore extends EventEmitter { public async getCsrs() { const filteredCsrsMap: Map = new Map() - // @ts-expect-error - OrbitDB's type declaration of `load` lacks 'options' - await this.store.load({ fetchEntryTimeout: 15000 }) const allEntries = this.store .iterator({ limit: -1 }) .collect() diff --git a/packages/backend/src/nest/storage/certificates/certificates.store.ts b/packages/backend/src/nest/storage/certificates/certificates.store.ts index c262e96a07..f8699c0651 100644 --- a/packages/backend/src/nest/storage/certificates/certificates.store.ts +++ b/packages/backend/src/nest/storage/certificates/certificates.store.ts @@ -41,7 +41,6 @@ export class CertificatesStore extends EventEmitter { write: ['*'], }, }) - await this.store.load() this.store.events.on('ready', async () => { this.logger('Loaded certificates to memory') @@ -59,8 +58,7 @@ export class CertificatesStore extends EventEmitter { await this.loadedCertificates() }) - // // @ts-expect-error - OrbitDB's type declaration of `load` lacks 'options' - // await this.store.load({ fetchEntryTimeout: 15000 }) + await this.store.load() this.logger('Initialized') } @@ -72,7 +70,9 @@ export class CertificatesStore extends EventEmitter { } public async close() { + this.logger('Closing certificates DB') await this.store?.close() + this.logger('Closed certificates DB') } public getAddress() { @@ -154,8 +154,6 @@ export class CertificatesStore extends EventEmitter { return [] } - // @ts-expect-error - OrbitDB's type declaration of `load` lacks 'options' - await this.store.load({ fetchEntryTimeout: 15000 }) const allCertificates = this.store .iterator({ limit: -1 }) .collect() diff --git a/packages/backend/src/nest/storage/communityMetadata/communityMetadata.store.ts b/packages/backend/src/nest/storage/communityMetadata/communityMetadata.store.ts index 9acbb661be..1b9ceca296 100644 --- a/packages/backend/src/nest/storage/communityMetadata/communityMetadata.store.ts +++ b/packages/backend/src/nest/storage/communityMetadata/communityMetadata.store.ts @@ -67,17 +67,13 @@ export class CommunityMetadataStore extends EventEmitter { this.store.events.on('replicated', async () => { logger('Replicated community metadata') - // @ts-expect-error - OrbitDB's type declaration of `load` lacks 'options' - // TODO: Is this necessary here? - await this.store.load({ fetchEntryTimeout: 15000 }) const meta = this.getCommunityMetadata() if (meta) { this.emit(StorageEvents.COMMUNITY_METADATA_STORED, meta) } }) - // @ts-expect-error - OrbitDB's type declaration of `load` lacks 'options' - await this.store.load({ fetchEntryTimeout: 15000 }) + await this.store.load() const meta = this.getCommunityMetadata() if (meta) { this.emit(StorageEvents.COMMUNITY_METADATA_STORED, meta) @@ -90,7 +86,9 @@ export class CommunityMetadataStore extends EventEmitter { } public async close() { + logger('Closing community metadata DB') await this.store?.close() + logger('Closed community metadata DB') } public async updateCommunityMetadata(newMeta: CommunityMetadata): Promise { diff --git a/packages/backend/src/nest/storage/storage.service.ts b/packages/backend/src/nest/storage/storage.service.ts index 7a8c1d95fb..9673dfb9c2 100644 --- a/packages/backend/src/nest/storage/storage.service.ts +++ b/packages/backend/src/nest/storage/storage.service.ts @@ -251,7 +251,9 @@ export class StorageService extends EventEmitter { public async stopOrbitDb() { try { + this.logger('Closing channels DB') await this.channels?.close() + this.logger('Closed channels DB') } catch (e) { this.logger.error('Error closing channels db', e) } @@ -352,8 +354,6 @@ export class StorageService extends EventEmitter { public async loadAllChannels() { this.logger('Getting all channels') - // @ts-expect-error - OrbitDB's type declaration of `load` lacks 'options' - await this.channels.load({ fetchEntryTimeout: 2000 }) this.emit(StorageEvents.CHANNELS_STORED, { channels: this.channels.all as unknown as { [key: string]: PublicChannel }, }) @@ -376,8 +376,6 @@ export class StorageService extends EventEmitter { this.channels.events.on('replicated', async () => { this.logger('REPLICATED: Channels') this.emit(SocketActionTypes.CONNECTION_PROCESS_INFO, ConnectionProcessInfo.CHANNELS_STORED) - // @ts-expect-error - OrbitDB's type declaration of `load` lacks 'options' - await this.channels.load({ fetchEntryTimeout: 2000 }) const channels = Object.values(this.channels.all) @@ -398,8 +396,7 @@ export class StorageService extends EventEmitter { }) }) - // @ts-expect-error - OrbitDB's type declaration of `load` lacks 'options' - await this.channels.load({ fetchEntryTimeout: 1000 }) + await this.channels.load() this.logger('Channels count:', Object.keys(this.channels.all).length) this.logger('Channels names:', Object.keys(this.channels.all)) Object.values(this.channels.all).forEach(async (channel: PublicChannel) => { @@ -544,6 +541,7 @@ export class StorageService extends EventEmitter { } }) + // FIXME: load is called twice for channel stores await db.load() repo.eventsAttached = true } @@ -601,8 +599,7 @@ export class StorageService extends EventEmitter { this.publicChannelsRepos.set(channelId, { db, eventsAttached: false }) this.logger(`Set ${channelId} to local channels`) - // @ts-expect-error - OrbitDB's type declaration of `load` lacks 'options' - await db.load({ fetchEntryTimeout: 2000 }) + await db.load() this.logger(`Created channel ${channelId}`) await this.subscribeToPubSub([StorageService.dbAddress(db.address)]) @@ -612,8 +609,6 @@ export class StorageService extends EventEmitter { public async deleteChannel(payload: { channelId: string; ownerPeerId: string }) { console.log('deleting channel storage', payload) const { channelId, ownerPeerId } = payload - // @ts-expect-error - OrbitDB's type declaration of `load` lacks 'options' - await this.channels.load({ fetchEntryTimeout: 15000 }) const channel = this.channels.get(channelId) if (!this.peerId) { this.logger('deleteChannel - peerId is null') @@ -636,18 +631,8 @@ export class StorageService extends EventEmitter { eventsAttached: false, } } - await repo.db.load() - // const allEntries = this.getAllEventLogRawEntries(repo.db) await repo.db.close() await repo.db.drop() - // const hashes = allEntries.map(e => CID.parse(e.hash)) - // const files = allEntries - // .map(e => { - // return e.payload.value.media - // }) - // .filter(isDefined) - // await this.deleteChannelFiles(files) - // await this.deleteChannelMessages(hashes) this.publicChannelsRepos.delete(channelId) return { channelId: payload.channelId } } @@ -689,6 +674,7 @@ export class StorageService extends EventEmitter { return } try { + this.logger('Sending message:', message.id) await repo.db.add(message) } catch (e) { this.logger.error( diff --git a/packages/backend/src/nest/storage/userProfile/userProfile.store.ts b/packages/backend/src/nest/storage/userProfile/userProfile.store.ts index aace9f027a..503baf45af 100644 --- a/packages/backend/src/nest/storage/userProfile/userProfile.store.ts +++ b/packages/backend/src/nest/storage/userProfile/userProfile.store.ts @@ -77,8 +77,7 @@ export class UserProfileStore extends EventEmitter { }) }) - // @ts-expect-error - OrbitDB's type declaration of `load` lacks 'options' - await this.store.load({ fetchEntryTimeout: 15000 }) + await this.store.load() } public getAddress() { @@ -86,7 +85,9 @@ export class UserProfileStore extends EventEmitter { } public async close() { + logger('Closing user profile DB') await this.store?.close() + logger('Closed user profile DB') } public async addUserProfile(userProfile: UserProfile) { diff --git a/packages/mobile/src/store/init/init.slice.ts b/packages/mobile/src/store/init/init.slice.ts index f9106199d6..92abfb4a45 100644 --- a/packages/mobile/src/store/init/init.slice.ts +++ b/packages/mobile/src/store/init/init.slice.ts @@ -44,9 +44,6 @@ export const initSlice = createSlice({ setStoreReady: state => { state.ready = true }, - setCryptoEngineInitialized: (state, action: PayloadAction) => { - state.isCryptoEngineInitialized = action.payload - }, updateInitDescription: (state, action: PayloadAction) => { state.initDescription = action.payload }, diff --git a/packages/mobile/src/store/init/setupCrypto/setupCrypto.saga.test.ts b/packages/mobile/src/store/init/setupCrypto/setupCrypto.saga.test.ts deleted file mode 100644 index efd5b18bb6..0000000000 --- a/packages/mobile/src/store/init/setupCrypto/setupCrypto.saga.test.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { combineReducers } from '@reduxjs/toolkit' -import { expectSaga } from 'redux-saga-test-plan' -import { call } from 'redux-saga-test-plan/matchers' -import { StoreKeys } from '../../store.keys' -import { initActions, initReducer, InitState } from '../init.slice' -import { initCryptoEngine, setupCryptoSaga } from './setupCrypto.saga' - -describe('setupCryptoSaga', () => { - test('should be defined', async () => { - await expectSaga(setupCryptoSaga) - .withReducer(combineReducers({ [StoreKeys.Init]: initReducer }), { - [StoreKeys.Init]: { - ...new InitState(), - }, - }) - .provide([[call.fn(initCryptoEngine), null]]) - .call(initCryptoEngine) - .put(initActions.setCryptoEngineInitialized(true)) - .hasFinalState({ - [StoreKeys.Init]: { - ...new InitState(), - isCryptoEngineInitialized: true, - }, - }) - .run() - }) -}) diff --git a/packages/mobile/src/store/init/setupCrypto/setupCrypto.saga.ts b/packages/mobile/src/store/init/setupCrypto/setupCrypto.saga.ts deleted file mode 100644 index 61320653a7..0000000000 --- a/packages/mobile/src/store/init/setupCrypto/setupCrypto.saga.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { setEngine, CryptoEngine } from 'pkijs' - -import { select, call, put } from 'typed-redux-saga' -import { initSelectors } from '../init.selectors' -import { initActions } from '../init.slice' - -export function* setupCryptoSaga(): Generator { - const isCryptoEngineInitialized = yield* select(initSelectors.isCryptoEngineInitialized) - if (!isCryptoEngineInitialized) { - yield* call(initCryptoEngine) - yield* put(initActions.setCryptoEngineInitialized(true)) - } -} - -export const initCryptoEngine = () => { - setEngine( - 'newEngine', - new CryptoEngine({ - name: '', - crypto, - subtle: crypto.subtle, - }) - ) -} diff --git a/packages/mobile/src/store/init/startConnection/startConnection.saga.ts b/packages/mobile/src/store/init/startConnection/startConnection.saga.ts index 44a48cfbc6..4f77f32552 100644 --- a/packages/mobile/src/store/init/startConnection/startConnection.saga.ts +++ b/packages/mobile/src/store/init/startConnection/startConnection.saga.ts @@ -1,5 +1,17 @@ import { io } from 'socket.io-client' -import { select, put, call, cancel, fork, takeEvery, FixedTask, delay, apply, putResolve } from 'typed-redux-saga' +import { + select, + put, + putResolve, + call, + cancel, + fork, + take, + takeLeading, + takeEvery, + FixedTask, + apply, +} from 'typed-redux-saga' import { PayloadAction } from '@reduxjs/toolkit' import { socket as stateManager, Socket } from '@quiet/state-manager' import { encodeSecret } from '@quiet/common' @@ -14,16 +26,6 @@ export function* startConnectionSaga( const isAlreadyConnected = yield* select(initSelectors.isWebsocketConnected) if (isAlreadyConnected) return - while (true) { - const isCryptoEngineInitialized = yield* select(initSelectors.isCryptoEngineInitialized) - console.log('WEBSOCKET', 'Waiting for crypto engine to initialize') - if (!isCryptoEngineInitialized) { - yield* delay(500) - } else { - break - } - } - const { dataPort, socketIOSecret } = action.payload console.log('WEBSOCKET', 'Entered start connection saga', dataPort) @@ -49,17 +51,20 @@ export function* startConnectionSaga( }) yield* fork(handleSocketLifecycleActions, socket, action.payload) // Handle opening/restoring connection - yield* takeEvery(initActions.setWebsocketConnected, setConnectedSaga, socket) + yield* takeLeading(initActions.setWebsocketConnected, setConnectedSaga, socket) } function* setConnectedSaga(socket: Socket): Generator { + console.log('Frontend is ready. Forking state-manager sagas and starting backend...') + const task = yield* fork(stateManager.useIO, socket) - console.log('WEBSOCKET', 'Forking state-manager sagas', task) - // Handle suspending current connection - yield* takeEvery(initActions.suspendWebsocketConnection, cancelRootTaskSaga, task) - console.log('Frontend is ready. Starting backend...') + // @ts-ignore - Why is this broken? yield* apply(socket, socket.emit, [SocketActionTypes.START]) + + // Handle suspending current connection + yield* take(initActions.suspendWebsocketConnection) + yield* call(cancelRootTaskSaga, task) } function* handleSocketLifecycleActions(socket: Socket, socketIOData: WebsocketConnectionPayload): Generator { diff --git a/packages/mobile/src/store/nativeServices/events/nativeServicesCallbacks.ts b/packages/mobile/src/store/nativeServices/events/nativeServicesCallbacks.ts index 23ab6b8ba5..fba6757ce7 100644 --- a/packages/mobile/src/store/nativeServices/events/nativeServicesCallbacks.ts +++ b/packages/mobile/src/store/nativeServices/events/nativeServicesCallbacks.ts @@ -1,5 +1,5 @@ import { eventChannel } from 'redux-saga' -import { call, put, take } from 'typed-redux-saga' +import { call, put, take, cancelled } from 'typed-redux-saga' import { app, publicChannels, WEBSOCKET_CONNECTION_CHANNEL, INIT_CHECK_CHANNEL, network } from '@quiet/state-manager' import { initActions, InitCheckPayload, WebsocketConnectionPayload } from '../../init/init.slice' import { ScreenNames } from '../../../const/ScreenNames.enum' @@ -9,10 +9,18 @@ import { navigationActions } from '../../navigation/navigation.slice' import { nativeServicesActions } from '../nativeServices.slice' export function* nativeServicesCallbacksSaga(): Generator { - const channel = yield* call(deviceEvents) - while (true) { - const action = yield* take(channel) - yield put(action) + console.log('nativeServicesCallbacksSaga starting') + try { + const channel = yield* call(deviceEvents) + while (true) { + const action = yield* take(channel) + yield put(action) + } + } finally { + console.log('nativeServicesCallbacksSaga stopping') + if (yield cancelled()) { + console.log('nativeServicesCallbacksSaga cancelled') + } } } diff --git a/packages/mobile/src/store/nativeServices/leaveCommunity/leaveCommunity.saga.ts b/packages/mobile/src/store/nativeServices/leaveCommunity/leaveCommunity.saga.ts index 56ec0c77f7..3d363a82ae 100644 --- a/packages/mobile/src/store/nativeServices/leaveCommunity/leaveCommunity.saga.ts +++ b/packages/mobile/src/store/nativeServices/leaveCommunity/leaveCommunity.saga.ts @@ -1,4 +1,4 @@ -import { select, call, takeLeading, putResolve } from 'typed-redux-saga' +import { select, call, putResolve } from 'typed-redux-saga' import { app } from '@quiet/state-manager' import { persistor } from '../../store' import { nativeServicesActions } from '../nativeServices.slice' @@ -9,11 +9,8 @@ import { ScreenNames } from '../../../../src/const/ScreenNames.enum' export function* leaveCommunitySaga(): Generator { console.log('Leaving community') - // Restart backend yield* putResolve(app.actions.closeServices()) - - yield takeLeading(initActions.canceledRootTask.type, clearReduxStore) } export function* clearReduxStore(): Generator { diff --git a/packages/mobile/src/store/nativeServices/nativeServices.master.saga.ts b/packages/mobile/src/store/nativeServices/nativeServices.master.saga.ts index 13f1fe441a..8544fbc563 100644 --- a/packages/mobile/src/store/nativeServices/nativeServices.master.saga.ts +++ b/packages/mobile/src/store/nativeServices/nativeServices.master.saga.ts @@ -1,13 +1,21 @@ -import { all, fork, takeEvery } from 'typed-redux-saga' +import { all, fork, takeEvery, cancelled } from 'typed-redux-saga' import { nativeServicesCallbacksSaga } from './events/nativeServicesCallbacks' import { leaveCommunitySaga } from './leaveCommunity/leaveCommunity.saga' import { flushPersistorSaga } from './flushPersistor/flushPersistor.saga' import { nativeServicesActions } from './nativeServices.slice' export function* nativeServicesMasterSaga(): Generator { - yield all([ - fork(nativeServicesCallbacksSaga), - takeEvery(nativeServicesActions.leaveCommunity.type, leaveCommunitySaga), - takeEvery(nativeServicesActions.flushPersistor.type, flushPersistorSaga), - ]) + console.log('nativeServicesMasterSaga starting') + try { + yield all([ + fork(nativeServicesCallbacksSaga), + takeEvery(nativeServicesActions.leaveCommunity.type, leaveCommunitySaga), + takeEvery(nativeServicesActions.flushPersistor.type, flushPersistorSaga), + ]) + } finally { + console.log('nativeServicesMasterSaga stopping') + if (yield cancelled()) { + console.log('nativeServicesMasterSaga cancelled') + } + } } diff --git a/packages/mobile/src/store/root.saga.ts b/packages/mobile/src/store/root.saga.ts index 9b2fffff52..e3978f3cf4 100644 --- a/packages/mobile/src/store/root.saga.ts +++ b/packages/mobile/src/store/root.saga.ts @@ -1,19 +1,62 @@ -import { all, takeEvery } from 'typed-redux-saga' +import { all, call, take, takeEvery, takeLeading, fork, cancelled } from 'typed-redux-saga' import { nativeServicesMasterSaga } from './nativeServices/nativeServices.master.saga' import { navigationMasterSaga } from './navigation/navigation.master.saga' import { initMasterSaga } from './init/init.master.saga' import { initActions } from './init/init.slice' -import { setupCryptoSaga } from './init/setupCrypto/setupCrypto.saga' import { publicChannels } from '@quiet/state-manager' import { showNotificationSaga } from './nativeServices/showNotification/showNotification.saga' +import { clearReduxStore } from './nativeServices/leaveCommunity/leaveCommunity.saga' +import { setEngine, CryptoEngine } from 'pkijs' + +const initCryptoEngine = () => { + setEngine( + 'newEngine', + new CryptoEngine({ + name: '', + crypto, + subtle: crypto.subtle, + }) + ) +} export function* rootSaga(): Generator { - yield all([ - takeEvery(initActions.setStoreReady.type, setupCryptoSaga), - takeEvery(initActions.setStoreReady.type, initMasterSaga), - takeEvery(initActions.setStoreReady.type, navigationMasterSaga), - takeEvery(initActions.setStoreReady.type, nativeServicesMasterSaga), - // Below line is reponsible for displaying notifications about messages from channels other than currently viewing one - takeEvery(publicChannels.actions.markUnreadChannel.type, showNotificationSaga), - ]) + console.log('rootSaga starting') + try { + console.log('Initializing crypto engine') + yield* call(initCryptoEngine) + // We don't want to start any sagas until the store is ready in + // case they use the store. Currently, we run these sagas once per + // application lifecycle. However, when we leave the community and + // clear the Redux store, if the Redux store is cleared while a + // saga is running, I suppose there is a possibility of corrupted + // state. Perhaps, it would make more sense to stop this saga, + // clear the store and then restart it, but that requires some + // refactoring. + yield* take(initActions.setStoreReady) + yield* call(storeReadySaga) + } finally { + console.log('rootSaga stopping') + if (yield cancelled()) { + console.log('rootSaga cancelled') + } + } +} + +function* storeReadySaga(): Generator { + console.log('storeReadySaga starting') + try { + yield all([ + fork(initMasterSaga), + fork(navigationMasterSaga), + fork(nativeServicesMasterSaga), + // Below line is reponsible for displaying notifications about messages from channels other than currently viewing one + takeEvery(publicChannels.actions.markUnreadChannel.type, showNotificationSaga), + takeLeading(initActions.canceledRootTask.type, clearReduxStore), + ]) + } finally { + console.log('storeReadySaga stopping') + if (yield cancelled()) { + console.log('storeReadySaga cancelled') + } + } } diff --git a/packages/mobile/src/store/store.ts b/packages/mobile/src/store/store.ts index 106867a42f..62cfcab17e 100644 --- a/packages/mobile/src/store/store.ts +++ b/packages/mobile/src/store/store.ts @@ -71,5 +71,6 @@ export const store = configureStore({ }) export const persistor = persistStore(store, {}, () => { + console.log('Redux store is ready!') store.dispatch(initActions.setStoreReady()) }) diff --git a/packages/state-manager/src/sagas/app/app.master.saga.ts b/packages/state-manager/src/sagas/app/app.master.saga.ts index 07dca44027..9775a83256 100644 --- a/packages/state-manager/src/sagas/app/app.master.saga.ts +++ b/packages/state-manager/src/sagas/app/app.master.saga.ts @@ -1,14 +1,22 @@ import { Socket } from '../../types' -import { all, takeEvery, takeLeading } from 'typed-redux-saga' +import { all, takeEvery, takeLeading, cancelled } from 'typed-redux-saga' import { appActions } from './app.slice' import { closeServicesSaga } from './closeServices.saga' import { stopBackendSaga } from './stopBackend/stopBackend.saga' import { loadMigrationDataSaga } from './loadMigrationData/loadMigrationData.saga' export function* appMasterSaga(socket: Socket): Generator { - yield* all([ - takeLeading(appActions.closeServices.type, closeServicesSaga, socket), - takeEvery(appActions.stopBackend.type, stopBackendSaga, socket), - takeEvery(appActions.loadMigrationData.type, loadMigrationDataSaga, socket), - ]) + console.log('appMasterSaga starting') + try { + yield* all([ + takeLeading(appActions.closeServices.type, closeServicesSaga, socket), + takeEvery(appActions.stopBackend.type, stopBackendSaga, socket), + takeEvery(appActions.loadMigrationData.type, loadMigrationDataSaga, socket), + ]) + } finally { + console.log('appMasterSaga stopping') + if (yield cancelled()) { + console.log('appMasterSaga cancelled') + } + } } diff --git a/packages/state-manager/src/sagas/appConnection/connection.master.saga.ts b/packages/state-manager/src/sagas/appConnection/connection.master.saga.ts index bd2f054422..b0da74eb5b 100644 --- a/packages/state-manager/src/sagas/appConnection/connection.master.saga.ts +++ b/packages/state-manager/src/sagas/appConnection/connection.master.saga.ts @@ -1,6 +1,14 @@ -import { all, fork } from 'typed-redux-saga' +import { all, fork, cancelled } from 'typed-redux-saga' import { uptimeSaga } from './uptime/uptime.saga' export function* connectionMasterSaga(): Generator { - yield all([fork(uptimeSaga)]) + console.log('connectionMasterSaga starting') + try { + yield all([fork(uptimeSaga)]) + } finally { + console.log('connectionMasterSaga stopping') + if (yield cancelled()) { + console.log('connectionMasterSaga cancelled') + } + } } diff --git a/packages/state-manager/src/sagas/communities/communities.master.saga.ts b/packages/state-manager/src/sagas/communities/communities.master.saga.ts index c5e1e1a275..a19dd3f20d 100644 --- a/packages/state-manager/src/sagas/communities/communities.master.saga.ts +++ b/packages/state-manager/src/sagas/communities/communities.master.saga.ts @@ -1,5 +1,5 @@ import { type Socket } from '../../types' -import { all, takeEvery } from 'typed-redux-saga' +import { all, takeEvery, cancelled } from 'typed-redux-saga' import { communitiesActions } from './communities.slice' import { connectionActions } from '../appConnection/connection.slice' import { createCommunitySaga } from './createCommunity/createCommunity.saga' @@ -7,10 +7,18 @@ import { initCommunities, launchCommunitySaga } from './launchCommunity/launchCo import { createNetworkSaga } from './createNetwork/createNetwork.saga' export function* communitiesMasterSaga(socket: Socket): Generator { - yield all([ - takeEvery(communitiesActions.createNetwork.type, createNetworkSaga, socket), - takeEvery(connectionActions.torBootstrapped.type, initCommunities), - takeEvery(communitiesActions.createCommunity.type, createCommunitySaga, socket), - takeEvery(communitiesActions.launchCommunity.type, launchCommunitySaga, socket), - ]) + console.log('communitiesMasterSaga starting') + try { + yield all([ + takeEvery(communitiesActions.createNetwork.type, createNetworkSaga, socket), + takeEvery(connectionActions.torBootstrapped.type, initCommunities), + takeEvery(communitiesActions.createCommunity.type, createCommunitySaga, socket), + takeEvery(communitiesActions.launchCommunity.type, launchCommunitySaga, socket), + ]) + } finally { + console.log('communitiesMasterSaga stopping') + if (yield cancelled()) { + console.log('communitiesMasterSaga cancelled') + } + } } diff --git a/packages/state-manager/src/sagas/communities/createCommunity/createCommunity.saga.ts b/packages/state-manager/src/sagas/communities/createCommunity/createCommunity.saga.ts index 8308e764f1..8ab3953d56 100644 --- a/packages/state-manager/src/sagas/communities/createCommunity/createCommunity.saga.ts +++ b/packages/state-manager/src/sagas/communities/createCommunity/createCommunity.saga.ts @@ -12,6 +12,8 @@ export function* createCommunitySaga( socket: Socket, action: PayloadAction['payload']> ): Generator { + console.log('Creating community') + let communityId: string = action.payload if (!communityId) { diff --git a/packages/state-manager/src/sagas/communities/createNetwork/createNetwork.saga.ts b/packages/state-manager/src/sagas/communities/createNetwork/createNetwork.saga.ts index 33003d6df5..a75d7fb9cf 100644 --- a/packages/state-manager/src/sagas/communities/createNetwork/createNetwork.saga.ts +++ b/packages/state-manager/src/sagas/communities/createNetwork/createNetwork.saga.ts @@ -12,11 +12,13 @@ export function* createNetworkSaga( socket: Socket, action: PayloadAction['payload']> ) { - console.log('create network saga') + console.log('Creating network') // Community IDs are only local identifiers + console.log('Generating community ID') const id = yield* call(generateId) + console.log('Emitting CREATE_NETWORK') const network = yield* apply(socket, socket.emitWithAck, applyEmitParams(SocketActionTypes.CREATE_NETWORK, id)) // TODO: Move CA generation to backend when creating Community @@ -29,6 +31,7 @@ export function* createNetworkSaga( const notBeforeDate = new Date(Date.UTC(2010, 11, 28, 10, 10, 10)) const notAfterDate = new Date(Date.UTC(2030, 11, 28, 10, 10, 10)) + console.log('Generating CA') CA = yield* call( createRootCA, new Time({ type: 0, value: notBeforeDate }), @@ -46,6 +49,7 @@ export function* createNetworkSaga( ownerOrbitDbIdentity: action.payload.ownerOrbitDbIdentity, } + console.log('Adding new community', id) yield* put(communitiesActions.addNewCommunity(community)) yield* put(communitiesActions.setCurrentCommunity(id)) @@ -65,5 +69,8 @@ export function* createNetworkSaga( joinTimestamp: null, } + console.log('Adding new identity', identity.id) yield* put(identityActions.addNewIdentity(identity)) + + console.log('Network created') } diff --git a/packages/state-manager/src/sagas/errors/errors.master.saga.ts b/packages/state-manager/src/sagas/errors/errors.master.saga.ts index 88b09d53b7..acaf09e588 100644 --- a/packages/state-manager/src/sagas/errors/errors.master.saga.ts +++ b/packages/state-manager/src/sagas/errors/errors.master.saga.ts @@ -1,7 +1,15 @@ -import { all, takeEvery } from 'typed-redux-saga' +import { all, takeEvery, cancelled } from 'typed-redux-saga' import { errorsActions } from './errors.slice' import { handleErrorsSaga } from './handleErrors/handleErrors.saga' export function* errorsMasterSaga(): Generator { - yield all([takeEvery(errorsActions.handleError.type, handleErrorsSaga)]) + console.log('errorsMasterSaga starting') + try { + yield all([takeEvery(errorsActions.handleError.type, handleErrorsSaga)]) + } finally { + console.log('errorsMasterSaga stopping') + if (yield cancelled()) { + console.log('errorsMasterSaga cancelled') + } + } } diff --git a/packages/state-manager/src/sagas/files/files.master.saga.ts b/packages/state-manager/src/sagas/files/files.master.saga.ts index 094e856eab..7b0a373084 100644 --- a/packages/state-manager/src/sagas/files/files.master.saga.ts +++ b/packages/state-manager/src/sagas/files/files.master.saga.ts @@ -1,5 +1,5 @@ import { type Socket } from '../../types' -import { all, takeEvery } from 'typed-redux-saga' +import { all, takeEvery, cancelled } from 'typed-redux-saga' import { checkForMissingFilesSaga } from './checkForMissingFiles/checkForMissingFiles.saga' import { resetTransferSpeedSaga } from './resetTransferSpeed/resetTransferSpeed.saga' import { updateMessageMediaSaga } from './updateMessageMedia/updateMessageMedia' @@ -14,15 +14,23 @@ import { messagesActions } from '../messages/messages.slice' import { sendFileMessageSaga } from './uploadFile/sendFileMessage.saga' export function* filesMasterSaga(socket: Socket): Generator { - yield all([ - takeEvery(networkActions.addInitializedCommunity.type, resetTransferSpeedSaga), - takeEvery(filesActions.checkForMissingFiles.type, checkForMissingFilesSaga, socket), - takeEvery(filesActions.uploadFile.type, sendFileMessageSaga), - takeEvery(messagesActions.addMessagesSendingStatus.type, uploadFileSaga, socket), - takeEvery(filesActions.cancelDownload.type, cancelDownloadSaga, socket), - takeEvery(filesActions.updateMessageMedia.type, updateMessageMediaSaga), - takeEvery(filesActions.downloadFile.type, downloadFileSaga, socket), - takeEvery(filesActions.broadcastHostedFile.type, broadcastHostedFileSaga, socket), - takeEvery(filesActions.deleteFilesFromChannel.type, deleteFilesFromChannelSaga, socket), - ]) + console.log('filesMasterSaga starting') + try { + yield all([ + takeEvery(networkActions.addInitializedCommunity.type, resetTransferSpeedSaga), + takeEvery(filesActions.checkForMissingFiles.type, checkForMissingFilesSaga, socket), + takeEvery(filesActions.uploadFile.type, sendFileMessageSaga), + takeEvery(messagesActions.addMessagesSendingStatus.type, uploadFileSaga, socket), + takeEvery(filesActions.cancelDownload.type, cancelDownloadSaga, socket), + takeEvery(filesActions.updateMessageMedia.type, updateMessageMediaSaga), + takeEvery(filesActions.downloadFile.type, downloadFileSaga, socket), + takeEvery(filesActions.broadcastHostedFile.type, broadcastHostedFileSaga, socket), + takeEvery(filesActions.deleteFilesFromChannel.type, deleteFilesFromChannelSaga, socket), + ]) + } finally { + console.log('filesMasterSaga stopping') + if (yield cancelled()) { + console.log('filesMasterSaga cancelled') + } + } } diff --git a/packages/state-manager/src/sagas/identity/identity.master.saga.ts b/packages/state-manager/src/sagas/identity/identity.master.saga.ts index cf3c116db7..4a4ddbbd03 100644 --- a/packages/state-manager/src/sagas/identity/identity.master.saga.ts +++ b/packages/state-manager/src/sagas/identity/identity.master.saga.ts @@ -1,5 +1,5 @@ import { type Socket } from '../../types' -import { all, takeEvery } from 'typed-redux-saga' +import { all, takeEvery, cancelled } from 'typed-redux-saga' import { identityActions } from './identity.slice' import { registerUsernameSaga } from './registerUsername/registerUsername.saga' import { verifyJoinTimestampSaga } from './verifyJoinTimestamp/verifyJoinTimestamp.saga' @@ -8,10 +8,18 @@ import { usersActions } from '../users/users.slice' import { updateCertificateSaga } from './updateCertificate/updateCertificate.saga' export function* identityMasterSaga(socket: Socket): Generator { - yield all([ - takeEvery(identityActions.registerUsername.type, registerUsernameSaga, socket), - takeEvery(identityActions.verifyJoinTimestamp.type, verifyJoinTimestampSaga), - takeEvery(identityActions.saveUserCsr.type, saveUserCsrSaga, socket), - takeEvery(usersActions.responseSendCertificates.type, updateCertificateSaga), - ]) + console.log('identityMasterSaga starting') + try { + yield all([ + takeEvery(identityActions.registerUsername.type, registerUsernameSaga, socket), + takeEvery(identityActions.verifyJoinTimestamp.type, verifyJoinTimestampSaga), + takeEvery(identityActions.saveUserCsr.type, saveUserCsrSaga, socket), + takeEvery(usersActions.responseSendCertificates.type, updateCertificateSaga), + ]) + } finally { + console.log('identityMasterSaga stopping') + if (yield cancelled()) { + console.log('identityMasterSaga cancelled') + } + } } diff --git a/packages/state-manager/src/sagas/identity/registerUsername/registerUsername.saga.ts b/packages/state-manager/src/sagas/identity/registerUsername/registerUsername.saga.ts index e6714f430c..0623bdc65e 100644 --- a/packages/state-manager/src/sagas/identity/registerUsername/registerUsername.saga.ts +++ b/packages/state-manager/src/sagas/identity/registerUsername/registerUsername.saga.ts @@ -29,7 +29,7 @@ export function* registerUsernameSaga( console.error('Could not register username, no community data') return } - console.log('Found community') + console.log('Found community', community.id) let identity = yield* select(identitySelectors.currentIdentity) if (!identity) { @@ -41,7 +41,7 @@ export function* registerUsernameSaga( console.error('Could not register username, no identity') return } - console.log('Found identity') + console.log('Found identity', identity.id) let userCsr = identity.userCsr @@ -66,6 +66,7 @@ export function* registerUsernameSaga( existingKeyPair, } + console.log('Recreating user CSR') userCsr = yield* call(createUserCsr, payload) } catch (e) { console.error(e) @@ -80,6 +81,8 @@ export function* registerUsernameSaga( signAlg: config.signAlg, hashAlg: config.hashAlg, } + + console.log('Creating user CSR') userCsr = yield* call(createUserCsr, payload) } catch (e) { console.error(e) @@ -96,6 +99,7 @@ export function* registerUsernameSaga( isUsernameTaken, } + console.log('Adding user CSR to Redux', payload.communityId) yield* put(identityActions.addCsr(payload)) if (community.CA?.rootCertString) { diff --git a/packages/state-manager/src/sagas/identity/saveUserCsr/saveUserCsr.saga.ts b/packages/state-manager/src/sagas/identity/saveUserCsr/saveUserCsr.saga.ts index 05c371bef0..0cc07fd1c7 100644 --- a/packages/state-manager/src/sagas/identity/saveUserCsr/saveUserCsr.saga.ts +++ b/packages/state-manager/src/sagas/identity/saveUserCsr/saveUserCsr.saga.ts @@ -7,8 +7,14 @@ export function* saveUserCsrSaga(socket: Socket): Generator { console.log('Saving user CSR') const identity = yield* select(identitySelectors.currentIdentity) - if (!identity?.userCsr) { - console.error('Cannot save user csr to backend, no userCsr') + + if (!identity) { + console.error('Cannot save user CSR to backend, no identity') + return + } + + if (!identity.userCsr) { + console.error('Cannot save user CSR to backend, no userCsr', identity) return } @@ -16,6 +22,6 @@ export function* saveUserCsrSaga(socket: Socket): Generator { csr: identity.userCsr?.userCsr, } - console.log(`Send ${SocketActionTypes.ADD_CSR}`) + console.log('Emitting ADD_CSR') yield* apply(socket, socket.emit, applyEmitParams(SocketActionTypes.ADD_CSR, payload)) } diff --git a/packages/state-manager/src/sagas/messages/messages.master.saga.ts b/packages/state-manager/src/sagas/messages/messages.master.saga.ts index 6f7f7e70c6..2235f985de 100644 --- a/packages/state-manager/src/sagas/messages/messages.master.saga.ts +++ b/packages/state-manager/src/sagas/messages/messages.master.saga.ts @@ -1,5 +1,4 @@ -import { takeEvery } from 'redux-saga/effects' -import { all } from 'typed-redux-saga' +import { all, takeEvery, cancelled } from 'typed-redux-saga' import { type Socket } from '../../types' import { messagesActions } from './messages.slice' import { sendMessageSaga } from './sendMessage/sendMessage.saga' @@ -16,18 +15,26 @@ import { autoDownloadFilesSaga } from '../files/autoDownloadFiles/autoDownloadFi import { sendDeletionMessageSaga } from './sendDeletionMessage/sendDeletionMessage.saga' export function* messagesMasterSaga(socket: Socket): Generator { - yield all([ - takeEvery(messagesActions.sendMessage.type, sendMessageSaga, socket), - takeEvery(messagesActions.addMessages.type, autoDownloadFilesSaga, socket), - takeEvery(messagesActions.addMessages.type, addMessagesSaga), - takeEvery(messagesActions.addMessages.type, verifyMessagesSaga), - takeEvery(messagesActions.addMessages.type, markUnreadChannelsSaga), - takeEvery(messagesActions.addMessages.type, updateNewestMessageSaga), - takeEvery(messagesActions.lazyLoading.type, lazyLoadingSaga), - takeEvery(messagesActions.extendCurrentPublicChannelCache.type, extendCurrentPublicChannelCacheSaga), - takeEvery(messagesActions.resetCurrentPublicChannelCache.type, resetCurrentPublicChannelCacheSaga), - takeEvery(messagesActions.checkForMessages.type, checkForMessagesSaga), - takeEvery(messagesActions.getMessages.type, getMessagesSaga, socket), - takeEvery(messagesActions.sendDeletionMessage.type, sendDeletionMessageSaga), - ]) + console.log('messagesMasterSaga starting') + try { + yield all([ + takeEvery(messagesActions.sendMessage.type, sendMessageSaga, socket), + takeEvery(messagesActions.addMessages.type, autoDownloadFilesSaga, socket), + takeEvery(messagesActions.addMessages.type, addMessagesSaga), + takeEvery(messagesActions.addMessages.type, verifyMessagesSaga), + takeEvery(messagesActions.addMessages.type, markUnreadChannelsSaga), + takeEvery(messagesActions.addMessages.type, updateNewestMessageSaga), + takeEvery(messagesActions.lazyLoading.type, lazyLoadingSaga), + takeEvery(messagesActions.extendCurrentPublicChannelCache.type, extendCurrentPublicChannelCacheSaga), + takeEvery(messagesActions.resetCurrentPublicChannelCache.type, resetCurrentPublicChannelCacheSaga), + takeEvery(messagesActions.checkForMessages.type, checkForMessagesSaga), + takeEvery(messagesActions.getMessages.type, getMessagesSaga, socket), + takeEvery(messagesActions.sendDeletionMessage.type, sendDeletionMessageSaga), + ]) + } finally { + console.log('messagesMasterSaga stopping') + if (yield cancelled()) { + console.log('messagesMasterSaga cancelled') + } + } } diff --git a/packages/state-manager/src/sagas/messages/sendMessage/sendMessage.saga.ts b/packages/state-manager/src/sagas/messages/sendMessage/sendMessage.saga.ts index fee94b4d50..21e7fbccd0 100644 --- a/packages/state-manager/src/sagas/messages/sendMessage/sendMessage.saga.ts +++ b/packages/state-manager/src/sagas/messages/sendMessage/sendMessage.saga.ts @@ -99,6 +99,7 @@ export function* sendMessageSaga( yield* take(publicChannelsActions.setChannelSubscribed) } + console.log('Emitting SEND_MESSAGE', id) yield* apply( socket, socket.emit, diff --git a/packages/state-manager/src/sagas/publicChannels/publicChannels.master.saga.ts b/packages/state-manager/src/sagas/publicChannels/publicChannels.master.saga.ts index d97db92bf4..675146bcff 100644 --- a/packages/state-manager/src/sagas/publicChannels/publicChannels.master.saga.ts +++ b/packages/state-manager/src/sagas/publicChannels/publicChannels.master.saga.ts @@ -1,5 +1,5 @@ import { type Socket } from '../../types' -import { all, takeEvery } from 'typed-redux-saga' +import { all, takeEvery, cancelled } from 'typed-redux-saga' import { publicChannelsActions } from './publicChannels.slice' import { createChannelSaga } from './createChannel/createChannel.saga' import { deleteChannelSaga } from './deleteChannel/deleteChannel.saga' @@ -11,14 +11,22 @@ import { channelDeletionResponseSaga } from './channelDeletionResponse/channelDe import { sendIntroductionMessageSaga } from './sendIntroductionMessage/sendIntroductionMessage.saga' export function* publicChannelsMasterSaga(socket: Socket): Generator { - yield all([ - takeEvery(publicChannelsActions.createChannel.type, createChannelSaga, socket), - takeEvery(publicChannelsActions.deleteChannel.type, deleteChannelSaga, socket), - takeEvery(publicChannelsActions.channelDeletionResponse.type, channelDeletionResponseSaga), - takeEvery(publicChannelsActions.createGeneralChannel.type, createGeneralChannelSaga), - takeEvery(publicChannelsActions.sendInitialChannelMessage.type, sendInitialChannelMessageSaga), - takeEvery(publicChannelsActions.channelsReplicated.type, channelsReplicatedSaga), - takeEvery(publicChannelsActions.setCurrentChannel.type, clearUnreadChannelsSaga), - takeEvery(publicChannelsActions.sendIntroductionMessage.type, sendIntroductionMessageSaga), - ]) + console.log('publicChannelsMasterSaga starting') + try { + yield all([ + takeEvery(publicChannelsActions.createChannel.type, createChannelSaga, socket), + takeEvery(publicChannelsActions.deleteChannel.type, deleteChannelSaga, socket), + takeEvery(publicChannelsActions.channelDeletionResponse.type, channelDeletionResponseSaga), + takeEvery(publicChannelsActions.createGeneralChannel.type, createGeneralChannelSaga), + takeEvery(publicChannelsActions.sendInitialChannelMessage.type, sendInitialChannelMessageSaga), + takeEvery(publicChannelsActions.channelsReplicated.type, channelsReplicatedSaga), + takeEvery(publicChannelsActions.setCurrentChannel.type, clearUnreadChannelsSaga), + takeEvery(publicChannelsActions.sendIntroductionMessage.type, sendIntroductionMessageSaga), + ]) + } finally { + console.log('publicChannelsMasterSaga stopping') + if (yield cancelled()) { + console.log('publicChannelsMasterSaga cancelled') + } + } } diff --git a/packages/state-manager/src/sagas/socket/startConnection/startConnection.saga.ts b/packages/state-manager/src/sagas/socket/startConnection/startConnection.saga.ts index a64a65224c..9e324ef158 100644 --- a/packages/state-manager/src/sagas/socket/startConnection/startConnection.saga.ts +++ b/packages/state-manager/src/sagas/socket/startConnection/startConnection.saga.ts @@ -1,6 +1,6 @@ import { eventChannel } from 'redux-saga' import { type Socket } from '../../../types' -import { all, call, fork, put, takeEvery } from 'typed-redux-saga' +import { all, call, fork, put, takeEvery, cancelled } from 'typed-redux-saga' import logger from '../../../utils/logger' import { appActions } from '../../app/app.slice' import { appMasterSaga } from '../../app/app.master.saga' @@ -176,23 +176,39 @@ export function subscribe(socket: Socket) { } export function* handleActions(socket: Socket): Generator { - const socketChannel = yield* call(subscribe, socket) - yield takeEvery(socketChannel, function* (action) { - yield put(action) - }) + console.log('handleActions starting') + try { + const socketChannel = yield* call(subscribe, socket) + yield takeEvery(socketChannel, function* (action) { + yield put(action) + }) + } finally { + console.log('handleActions stopping') + if (yield cancelled()) { + console.log('handleActions cancelled') + } + } } export function* useIO(socket: Socket): Generator { - yield all([ - fork(handleActions, socket), - fork(publicChannelsMasterSaga, socket), - fork(messagesMasterSaga, socket), - fork(filesMasterSaga, socket), - fork(identityMasterSaga, socket), - fork(communitiesMasterSaga, socket), - fork(usersMasterSaga, socket), - fork(appMasterSaga, socket), - fork(connectionMasterSaga), - fork(errorsMasterSaga), - ]) + console.log('useIO starting') + try { + yield all([ + fork(handleActions, socket), + fork(publicChannelsMasterSaga, socket), + fork(messagesMasterSaga, socket), + fork(filesMasterSaga, socket), + fork(identityMasterSaga, socket), + fork(communitiesMasterSaga, socket), + fork(usersMasterSaga, socket), + fork(appMasterSaga, socket), + fork(connectionMasterSaga), + fork(errorsMasterSaga), + ]) + } finally { + console.log('useIO stopping') + if (yield cancelled()) { + console.log('useIO cancelled') + } + } } diff --git a/packages/state-manager/src/sagas/users/users.master.saga.ts b/packages/state-manager/src/sagas/users/users.master.saga.ts index 72c7383ef3..947b95615a 100644 --- a/packages/state-manager/src/sagas/users/users.master.saga.ts +++ b/packages/state-manager/src/sagas/users/users.master.saga.ts @@ -1,9 +1,17 @@ -import { takeEvery } from 'redux-saga/effects' +import { takeEvery, cancelled } from 'redux-saga/effects' import { all } from 'typed-redux-saga' import { type Socket } from '../../types' import { usersActions } from './users.slice' import { saveUserProfileSaga } from './userProfile/saveUserProfile.saga' export function* usersMasterSaga(socket: Socket): Generator { - yield all([takeEvery(usersActions.saveUserProfile.type, saveUserProfileSaga, socket)]) + console.log('usersMasterSaga starting') + try { + yield all([takeEvery(usersActions.saveUserProfile.type, saveUserProfileSaga, socket)]) + } finally { + console.log('usersMasterSaga stopping') + if (yield cancelled()) { + console.log('usersMasterSaga cancelled') + } + } } From c317ffb1d8b431daf71c0dc5a5f4acde3895a49a Mon Sep 17 00:00:00 2001 From: Isla Koenigsknecht Date: Mon, 13 May 2024 18:22:07 -0400 Subject: [PATCH 33/48] Fix queue processing (#2525) * Process queue fixes and updates * Remove unnecessary log * Log when we push to queue --- .../connections-manager.service.ts | 28 ++-- .../backend/src/nest/libp2p/libp2p.service.ts | 23 +++- .../nest/libp2p/process-in-chunks.service.ts | 124 ++++++++++++------ .../src/nest/libp2p/process-in-chunks.spec.ts | 30 +++-- 4 files changed, 138 insertions(+), 67 deletions(-) diff --git a/packages/backend/src/nest/connections-manager/connections-manager.service.ts b/packages/backend/src/nest/connections-manager/connections-manager.service.ts index a030206be7..20ebdc3c21 100644 --- a/packages/backend/src/nest/connections-manager/connections-manager.service.ts +++ b/packages/backend/src/nest/connections-manager/connections-manager.service.ts @@ -263,20 +263,24 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI public async resume() { this.logger('Resuming!') await this.openSocket() - this.logger('Attempting to redial peers!') + const peersToDial = await this.getPeersOnResume() + this.libp2pService?.resume(peersToDial) + } + + public async getPeersOnResume(): Promise { + this.logger('Getting peers to redial') if (this.peerInfo && (this.peerInfo?.connected.length !== 0 || this.peerInfo?.dialed.length !== 0)) { - this.logger('Dialing peers with info from pause: ', this.peerInfo) - await this.libp2pService?.redialPeers([...this.peerInfo.connected, ...this.peerInfo.dialed]) - } else { - this.logger('Dialing peers from stored community (if exists)') - const community = await this.localDbService.getCurrentCommunity() - if (!community) { - this.logger(`No community launched, can't redial`) - return - } - const sortedPeers = await this.localDbService.getSortedPeers(community.peerList ?? []) - await this.libp2pService?.redialPeers(sortedPeers) + this.logger('Found peer info from pause: ', this.peerInfo) + return [...this.peerInfo.connected, ...this.peerInfo.dialed] + } + + this.logger('Getting peers from stored community (if exists)') + const community = await this.localDbService.getCurrentCommunity() + if (!community) { + this.logger(`No community launched, no peers found`) + return [] } + return await this.localDbService.getSortedPeers(community.peerList ?? []) } // This method is only used on iOS through rn-bridge for reacting on lifecycle changes diff --git a/packages/backend/src/nest/libp2p/libp2p.service.ts b/packages/backend/src/nest/libp2p/libp2p.service.ts index dcec245cd4..47a90e1487 100644 --- a/packages/backend/src/nest/libp2p/libp2p.service.ts +++ b/packages/backend/src/nest/libp2p/libp2p.service.ts @@ -62,9 +62,21 @@ export class Libp2pService extends EventEmitter { await this.hangUpPeers(peerInfo.dialed) this.dialedPeers.clear() this.connectedPeers.clear() + this.processInChunksService.pause() return peerInfo } + public resume = async (peersToDial: string[]): Promise => { + this.processInChunksService.resume() + if (peersToDial.length === 0) { + this.logger('No peers to redial!') + return + } + + this.logger(`Redialing ${peersToDial.length} peers`) + await this.redialPeers(peersToDial) + } + public readonly createLibp2pAddress = (address: string, peerId: string): string => { return createLibp2pAddress(address, peerId) } @@ -138,8 +150,7 @@ export class Libp2pService extends EventEmitter { // TODO: Sort peers await this.hangUpPeers(dialed) - this.processInChunksService.updateData(toDial) - await this.processInChunksService.process() + this.processInChunksService.updateQueue(toDial) } public async createInstance(params: Libp2pNodeParams): Promise { @@ -208,13 +219,12 @@ export class Libp2pService extends EventEmitter { this.on(Libp2pEvents.DIAL_PEERS, async (addresses: string[]) => { const nonDialedAddresses = addresses.filter(peerAddress => !this.dialedPeers.has(peerAddress)) this.logger('Dialing', nonDialedAddresses.length, 'addresses') - this.processInChunksService.updateData(nonDialedAddresses) - await this.processInChunksService.process() + this.processInChunksService.updateQueue(nonDialedAddresses) }) this.logger(`Initializing libp2p for ${peerId.toString()}, bootstrapping with ${peers.length} peers`) this.serverIoProvider.io.emit(SocketActionTypes.CONNECTION_PROCESS_INFO, ConnectionProcessInfo.INITIALIZING_LIBP2P) - this.processInChunksService.init(peers, this.dialPeer) + this.processInChunksService.init([], this.dialPeer) this.libp2pInstance.addEventListener('peer:discovery', peer => { this.logger(`${peerId.toString()} discovered ${peer.detail.id}`) @@ -268,7 +278,7 @@ export class Libp2pService extends EventEmitter { this.emit(Libp2pEvents.PEER_DISCONNECTED, peerStat) }) - await this.processInChunksService.process() + this.processInChunksService.updateQueue(peers) this.logger(`Initialized libp2p for peer ${peerId.toString()}`) } @@ -276,6 +286,7 @@ export class Libp2pService extends EventEmitter { public async close(): Promise { this.logger('Closing libp2p service') await this.libp2pInstance?.stop() + this.processInChunksService.pause() this.libp2pInstance = null this.connectedPeers = new Map() this.dialedPeers = new Set() diff --git a/packages/backend/src/nest/libp2p/process-in-chunks.service.ts b/packages/backend/src/nest/libp2p/process-in-chunks.service.ts index bad92b873d..f83fd8d218 100644 --- a/packages/backend/src/nest/libp2p/process-in-chunks.service.ts +++ b/packages/backend/src/nest/libp2p/process-in-chunks.service.ts @@ -1,8 +1,8 @@ import { EventEmitter } from 'events' -import fastq from 'fastq' -import type { queue, done } from 'fastq' +import fastq, { queueAsPromised } from 'fastq' import Logger from '../common/logger' +import { randomUUID } from 'crypto' const DEFAULT_CHUNK_SIZE = 10 export const DEFAULT_NUM_TRIES = 2 @@ -10,13 +10,14 @@ export const DEFAULT_NUM_TRIES = 2 type ProcessTask = { data: T tries: number + taskId: string } export class ProcessInChunksService extends EventEmitter { private isActive: boolean - private data: Set = new Set() private chunkSize: number - private taskQueue: queue> + private taskQueue: queueAsPromised> + private deadLetterQueue: ProcessTask[] = [] private processItem: (arg: T) => Promise private readonly logger = Logger(ProcessInChunksService.name) constructor() { @@ -27,59 +28,106 @@ export class ProcessInChunksService extends EventEmitter { this.logger(`Initializing process-in-chunks.service with peers ${JSON.stringify(data, null, 2)}`) this.processItem = processItem this.chunkSize = chunkSize - this.taskQueue = fastq(this, this.processOneItem, this.chunkSize) - this.updateData(data) - this.addToTaskQueue() + this.taskQueue = fastq.promise(this, this.processOneItem, this.chunkSize) + this.isActive = true + this.updateQueue(data) } - public updateData(items: T[]) { - this.logger(`Updating data with ${items.length} items`) - this.taskQueue.pause() - items.forEach(item => this.data.add(item)) - this.addToTaskQueue() + public updateQueue(items: T[]) { + this.logger(`Adding ${items.length} items to the task queue`) + items.forEach(item => this.addToTaskQueue(item)) } - private addToTaskQueue() { - this.logger(`Adding ${this.data.size} items to the task queue`) - for (const item of this.data) { - if (item) { - this.logger(`Adding data ${item} to the task queue`) - this.data.delete(item) - try { - this.taskQueue.push({ data: item, tries: 0 } as ProcessTask) - } catch (e) { - this.logger.error(`Error occurred while adding new task for item ${item} to the queue`, e) - this.data.add(item) - } + private async addToTaskQueue(task: ProcessTask): Promise + private async addToTaskQueue(item: T): Promise + private async addToTaskQueue(itemOrTask: T | ProcessTask): Promise { + if (!itemOrTask) { + this.logger.error('Item/task is null or undefined, skipping!') + return + } + + let task: ProcessTask + if ((itemOrTask as ProcessTask).taskId != null) { + task = itemOrTask as ProcessTask + } else { + this.logger(`Creating new task for ${itemOrTask}`) + task = { data: itemOrTask as T, tries: 0, taskId: randomUUID() } + } + + if (!this.isActive) { + this.logger( + 'ProcessInChunksService is not active, adding tasks to the dead letter queue!\n\nWARNING: You must call "resume" on the ProcessInChunksService to process the dead letter queue!!!' + ) + this.deadLetterQueue.push(task) + this.logger(`There are now ${this.deadLetterQueue.length} items in the dead letter queue`) + return + } + + this.logger(`Adding task ${task.taskId} with data ${task.data} to the task queue`) + try { + const success = await this.pushToQueueAndRun(task) + if (!success) { + this.logger(`Will try to re-attempt task ${task.taskId} with data ${task.data}`) + await this.pushToQueueAndRun({ ...task, tries: task.tries + 1 }) } + } catch (e) { + this.logger.error(`Error occurred while adding new task ${task.taskId} with data ${task.data} to the queue`, e) } } - public async processOneItem(task: ProcessTask) { + public async processOneItem(task: ProcessTask): Promise { + let success: boolean = false try { - this.logger(`Processing task with data ${task.data}`) + this.logger(`Processing task ${task.taskId} with data ${task.data}`) await this.processItem(task.data) + success = true } catch (e) { - this.logger.error(`Processing task with data ${task.data} failed`, e) - if (task.tries + 1 < DEFAULT_NUM_TRIES) { - this.logger(`Will try to re-attempt task with data ${task.data}`) - this.taskQueue.push({ ...task, tries: task.tries + 1 }) - } + this.logger.error(`Processing task ${task.taskId} with data ${task.data} failed`, e) } finally { this.logger(`Done attempting to process task with data ${task.data}`) } + return success } - public async process() { - this.logger(`Processing ${this.taskQueue.length()} items`) - this.taskQueue.resume() + private async pushToQueueAndRun(task: ProcessTask): Promise { + this.logger( + `Pushing task ${task.taskId} to queue, there will now be ${this.taskQueue.length() + 1} items in the queue` + ) + const success = await this.taskQueue.push(task) + if (success) { + this.logger(`Task ${task.taskId} completed successfully`) + } else { + this.logger(`Task ${task.taskId} failed`) + } + return success } - public stop() { + public resume() { if (this.isActive) { - this.logger('Stopping initial dial') - this.isActive = false - this.taskQueue.pause() + this.logger('ProcessInChunksService is already active') + return + } + + this.logger('Resuming ProcessInChunksService') + this.isActive = true + this.taskQueue.resume() + if (this.deadLetterQueue) { + this.logger(`Adding ${this.deadLetterQueue.length} tasks from the dead letter queue to the task queue`) + this.deadLetterQueue.forEach(task => this.addToTaskQueue(task)) + this.deadLetterQueue = [] } } + + public pause() { + if (!this.isActive) { + this.logger('ProcessInChunksService is already paused') + return + } + + this.logger('Pausing ProcessInChunksService') + this.isActive = false + this.deadLetterQueue = this.taskQueue.getQueue() + this.taskQueue.kill() + this.taskQueue.pause() + } } diff --git a/packages/backend/src/nest/libp2p/process-in-chunks.spec.ts b/packages/backend/src/nest/libp2p/process-in-chunks.spec.ts index ce751afdf3..24d05dd42f 100644 --- a/packages/backend/src/nest/libp2p/process-in-chunks.spec.ts +++ b/packages/backend/src/nest/libp2p/process-in-chunks.spec.ts @@ -3,6 +3,7 @@ import { ProcessInChunksService } from './process-in-chunks.service' import waitForExpect from 'wait-for-expect' import { TestModule } from '../common/test.module' import { Test, TestingModule } from '@nestjs/testing' +import { sleep } from '../common/sleep' describe('ProcessInChunks', () => { let module: TestingModule let processInChunks: ProcessInChunksService @@ -25,7 +26,6 @@ describe('ProcessInChunks', () => { .mockResolvedValueOnce() .mockRejectedValueOnce(new Error('Rejected 2')) processInChunks.init(['a', 'b', 'c', 'd'], mockProcessItem) - await processInChunks.process() await waitForExpect(() => { expect(mockProcessItem).toBeCalledTimes(6) }) @@ -39,9 +39,7 @@ describe('ProcessInChunks', () => { .mockResolvedValueOnce() .mockRejectedValueOnce(new Error('Rejected 1')) processInChunks.init(['a', 'b'], mockProcessItem) - await processInChunks.process() - processInChunks.updateData(['e', 'f']) - await processInChunks.process() + processInChunks.updateQueue(['e', 'f']) await waitForExpect(() => { expect(mockProcessItem).toBeCalledTimes(5) }) @@ -58,18 +56,28 @@ describe('ProcessInChunks', () => { .mockRejectedValueOnce(new Error('Rejected 2')) const chunkSize = 2 processInChunks.init(['a', 'b', 'c', 'd'], mockProcessItem, chunkSize) - await processInChunks.process() + await sleep(10000) await waitForExpect(() => { - expect(mockProcessItem).toBeCalledTimes(2) + expect(mockProcessItem).toBeCalledTimes(6) }) }) - it.skip('does not process more data if stopped', async () => { + it('does not process more data if stopped', async () => { const mockProcessItem = jest.fn(async () => {}) - const processInChunks = new ProcessInChunksService() - processInChunks.init(['a', 'b', 'c', 'd'], mockProcessItem) - processInChunks.stop() - await processInChunks.process() + processInChunks.init([], mockProcessItem) + processInChunks.pause() + processInChunks.updateQueue(['a', 'b', 'c', 'd']) expect(mockProcessItem).not.toBeCalled() }) + + it('processes tasks after resuming from pause', async () => { + const mockProcessItem = jest.fn(async () => {}) + processInChunks.init([], mockProcessItem) + processInChunks.pause() + processInChunks.updateQueue(['a', 'b', 'c', 'd']) + processInChunks.resume() + await waitForExpect(() => { + expect(mockProcessItem).toBeCalledTimes(4) + }) + }) }) From c293ccbc4503654287d09283510e9e8d0e2ded6a Mon Sep 17 00:00:00 2001 From: Lucas Leblow Date: Tue, 14 May 2024 14:46:36 -0600 Subject: [PATCH 34/48] Publish - @quiet/desktop@2.2.0-alpha.5 - @quiet/mobile@2.2.0-alpha.5 --- packages/desktop/CHANGELOG.md | 13 +++++++++++++ packages/desktop/package-lock.json | 4 ++-- packages/desktop/package.json | 2 +- packages/mobile/CHANGELOG.md | 16 ++++++++++++++++ packages/mobile/android/app/build.gradle | 4 ++-- packages/mobile/ios/Quiet/Info.plist | 2 +- packages/mobile/ios/QuietTests/Info.plist | 2 +- packages/mobile/package-lock.json | 4 ++-- packages/mobile/package.json | 2 +- 9 files changed, 39 insertions(+), 10 deletions(-) diff --git a/packages/desktop/CHANGELOG.md b/packages/desktop/CHANGELOG.md index dbfe219b55..32873225e4 100644 --- a/packages/desktop/CHANGELOG.md +++ b/packages/desktop/CHANGELOG.md @@ -1,3 +1,16 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +# [2.2.0-alpha.5](https://github.com/TryQuiet/quiet/compare/@quiet/desktop@2.2.0-alpha.4...@quiet/desktop@2.2.0-alpha.5) (2024-05-14) + +**Note:** Version bump only for package @quiet/desktop + + + + + [unreleased] # New features: diff --git a/packages/desktop/package-lock.json b/packages/desktop/package-lock.json index c7404ce111..3c1d17aa14 100644 --- a/packages/desktop/package-lock.json +++ b/packages/desktop/package-lock.json @@ -1,12 +1,12 @@ { "name": "@quiet/desktop", - "version": "2.2.0-alpha.4", + "version": "2.2.0-alpha.5", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@quiet/desktop", - "version": "2.2.0-alpha.4", + "version": "2.2.0-alpha.5", "license": "GPL-3.0-or-later", "dependencies": { "@electron/remote": "^2.0.8", diff --git a/packages/desktop/package.json b/packages/desktop/package.json index 7b648586a6..ec6a442fcf 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -80,7 +80,7 @@ }, "homepage": "https://github.com/TryQuiet", "@comment version": "To build new version for specific platform, just replace platform in version tag to one of following linux, mac, windows", - "version": "2.2.0-alpha.4", + "version": "2.2.0-alpha.5", "description": "Decentralized team chat", "main": "dist/main/main.js", "scripts": { diff --git a/packages/mobile/CHANGELOG.md b/packages/mobile/CHANGELOG.md index dbfe219b55..6f4da53c02 100644 --- a/packages/mobile/CHANGELOG.md +++ b/packages/mobile/CHANGELOG.md @@ -1,3 +1,19 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +# [2.2.0-alpha.5](https://github.com/TryQuiet/quiet/compare/@quiet/mobile@2.2.0-alpha.4...@quiet/mobile@2.2.0-alpha.5) (2024-05-14) + + +### Bug Fixes + +* Reorder the closing of services, prevent sagas running multiple times and close backend server properly ([#2499](https://github.com/TryQuiet/quiet/issues/2499)) ([134fbcb](https://github.com/TryQuiet/quiet/commit/134fbcb3249ffb4743e0fcfb30b08be355440566)) + + + + + [unreleased] # New features: diff --git a/packages/mobile/android/app/build.gradle b/packages/mobile/android/app/build.gradle index 47445b66e8..227a293444 100644 --- a/packages/mobile/android/app/build.gradle +++ b/packages/mobile/android/app/build.gradle @@ -192,8 +192,8 @@ android { applicationId "com.quietmobile" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 416 - versionName "2.2.0-alpha.4" + versionCode 417 + versionName "2.2.0-alpha.5" resValue "string", "build_config_package", "com.quietmobile" testBuildType System.getProperty('testBuildType', 'debug') testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' diff --git a/packages/mobile/ios/Quiet/Info.plist b/packages/mobile/ios/Quiet/Info.plist index 54ba75c093..26e639709b 100644 --- a/packages/mobile/ios/Quiet/Info.plist +++ b/packages/mobile/ios/Quiet/Info.plist @@ -34,7 +34,7 @@ CFBundleVersion - 373 + 374 ITSAppUsesNonExemptEncryption LSRequiresIPhoneOS diff --git a/packages/mobile/ios/QuietTests/Info.plist b/packages/mobile/ios/QuietTests/Info.plist index 536de9c787..d20d84e9ff 100644 --- a/packages/mobile/ios/QuietTests/Info.plist +++ b/packages/mobile/ios/QuietTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 373 + 374 diff --git a/packages/mobile/package-lock.json b/packages/mobile/package-lock.json index 3ef4481a0c..b443948b8d 100644 --- a/packages/mobile/package-lock.json +++ b/packages/mobile/package-lock.json @@ -1,12 +1,12 @@ { "name": "@quiet/mobile", - "version": "2.2.0-alpha.4", + "version": "2.2.0-alpha.5", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@quiet/mobile", - "version": "2.2.0-alpha.4", + "version": "2.2.0-alpha.5", "dependencies": { "@peculiar/webcrypto": "^1.4.3", "@react-native-clipboard/clipboard": "^1.13.2", diff --git a/packages/mobile/package.json b/packages/mobile/package.json index 5f8603da74..04b3347d2c 100644 --- a/packages/mobile/package.json +++ b/packages/mobile/package.json @@ -1,6 +1,6 @@ { "name": "@quiet/mobile", - "version": "2.2.0-alpha.4", + "version": "2.2.0-alpha.5", "scripts": { "build": "tsc -p tsconfig.build.json --noEmit", "storybook-android": "ENVFILE=.env.storybook react-native run-android --mode=storybookDebug --appIdSuffix=storybook.debug", From 18d5e7dd518b8d0fd9a0970251cded6baa1be586 Mon Sep 17 00:00:00 2001 From: Lucas Leblow Date: Tue, 14 May 2024 14:46:40 -0600 Subject: [PATCH 35/48] Update packages CHANGELOG.md --- packages/desktop/CHANGELOG.md | 14 +------------- packages/mobile/CHANGELOG.md | 17 +---------------- 2 files changed, 2 insertions(+), 29 deletions(-) diff --git a/packages/desktop/CHANGELOG.md b/packages/desktop/CHANGELOG.md index 32873225e4..36baaed49a 100644 --- a/packages/desktop/CHANGELOG.md +++ b/packages/desktop/CHANGELOG.md @@ -1,16 +1,3 @@ -# Change Log - -All notable changes to this project will be documented in this file. -See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - -# [2.2.0-alpha.5](https://github.com/TryQuiet/quiet/compare/@quiet/desktop@2.2.0-alpha.4...@quiet/desktop@2.2.0-alpha.5) (2024-05-14) - -**Note:** Version bump only for package @quiet/desktop - - - - - [unreleased] # New features: @@ -38,6 +25,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline * Fix issues with recreating general channel when deleted while offline ([#2334](https://github.com/TryQuiet/quiet/issues/2334)) * Fix package.json license inconsistency * Fixes issue with reconnecting to peers on resume on iOS ([#2424](https://github.com/TryQuiet/quiet/issues/2424)) +* Reorder the closing of services, prevent sagas running multiple times and close backend server properly [2.1.2] diff --git a/packages/mobile/CHANGELOG.md b/packages/mobile/CHANGELOG.md index 6f4da53c02..36baaed49a 100644 --- a/packages/mobile/CHANGELOG.md +++ b/packages/mobile/CHANGELOG.md @@ -1,19 +1,3 @@ -# Change Log - -All notable changes to this project will be documented in this file. -See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - -# [2.2.0-alpha.5](https://github.com/TryQuiet/quiet/compare/@quiet/mobile@2.2.0-alpha.4...@quiet/mobile@2.2.0-alpha.5) (2024-05-14) - - -### Bug Fixes - -* Reorder the closing of services, prevent sagas running multiple times and close backend server properly ([#2499](https://github.com/TryQuiet/quiet/issues/2499)) ([134fbcb](https://github.com/TryQuiet/quiet/commit/134fbcb3249ffb4743e0fcfb30b08be355440566)) - - - - - [unreleased] # New features: @@ -41,6 +25,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline * Fix issues with recreating general channel when deleted while offline ([#2334](https://github.com/TryQuiet/quiet/issues/2334)) * Fix package.json license inconsistency * Fixes issue with reconnecting to peers on resume on iOS ([#2424](https://github.com/TryQuiet/quiet/issues/2424)) +* Reorder the closing of services, prevent sagas running multiple times and close backend server properly [2.1.2] From c24c730b3b6d2e5baaa833aa8698e4d74dac7854 Mon Sep 17 00:00:00 2001 From: Isla Koenigsknecht Date: Thu, 16 May 2024 13:30:42 -0400 Subject: [PATCH 36/48] Use xcode 15 to get ios 17 sdk on builds (#2535) --- .github/workflows/mobile-deploy-ios.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/mobile-deploy-ios.yml b/.github/workflows/mobile-deploy-ios.yml index 657cc5df20..f3ed752a98 100644 --- a/.github/workflows/mobile-deploy-ios.yml +++ b/.github/workflows/mobile-deploy-ios.yml @@ -14,7 +14,7 @@ jobs: strategy: matrix: - os: [macos-12] + os: [macos-latest] steps: - name: "Print OS" @@ -30,7 +30,7 @@ jobs: - name: Setup XCode uses: maxim-lobanov/setup-xcode@9a697e2b393340c3cacd97468baa318e4c883d98 # v1.5.1 with: - xcode-version: '14.2' + xcode-version: '15.2' - name: Setup environment uses: ./.github/actions/setup-env From da26e8a946f012272e4a10a73d415b6838f60f27 Mon Sep 17 00:00:00 2001 From: Lucas Leblow Date: Thu, 16 May 2024 12:23:10 -0600 Subject: [PATCH 37/48] Publish - @quiet/desktop@2.2.0-alpha.6 - @quiet/mobile@2.2.0-alpha.6 --- packages/desktop/CHANGELOG.md | 13 +++++++++++++ packages/desktop/package-lock.json | 4 ++-- packages/desktop/package.json | 2 +- packages/mobile/CHANGELOG.md | 13 +++++++++++++ packages/mobile/android/app/build.gradle | 4 ++-- packages/mobile/ios/Quiet/Info.plist | 2 +- packages/mobile/ios/QuietTests/Info.plist | 2 +- packages/mobile/package-lock.json | 4 ++-- packages/mobile/package.json | 2 +- 9 files changed, 36 insertions(+), 10 deletions(-) diff --git a/packages/desktop/CHANGELOG.md b/packages/desktop/CHANGELOG.md index 36baaed49a..e768391b6c 100644 --- a/packages/desktop/CHANGELOG.md +++ b/packages/desktop/CHANGELOG.md @@ -1,3 +1,16 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +# [2.2.0-alpha.6](/compare/@quiet/desktop@2.2.0-alpha.5...@quiet/desktop@2.2.0-alpha.6) (2024-05-16) + +**Note:** Version bump only for package @quiet/desktop + + + + + [unreleased] # New features: diff --git a/packages/desktop/package-lock.json b/packages/desktop/package-lock.json index 3c1d17aa14..92fdef863e 100644 --- a/packages/desktop/package-lock.json +++ b/packages/desktop/package-lock.json @@ -1,12 +1,12 @@ { "name": "@quiet/desktop", - "version": "2.2.0-alpha.5", + "version": "2.2.0-alpha.6", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@quiet/desktop", - "version": "2.2.0-alpha.5", + "version": "2.2.0-alpha.6", "license": "GPL-3.0-or-later", "dependencies": { "@electron/remote": "^2.0.8", diff --git a/packages/desktop/package.json b/packages/desktop/package.json index ec6a442fcf..a1b16734cc 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -80,7 +80,7 @@ }, "homepage": "https://github.com/TryQuiet", "@comment version": "To build new version for specific platform, just replace platform in version tag to one of following linux, mac, windows", - "version": "2.2.0-alpha.5", + "version": "2.2.0-alpha.6", "description": "Decentralized team chat", "main": "dist/main/main.js", "scripts": { diff --git a/packages/mobile/CHANGELOG.md b/packages/mobile/CHANGELOG.md index 36baaed49a..d362e9f862 100644 --- a/packages/mobile/CHANGELOG.md +++ b/packages/mobile/CHANGELOG.md @@ -1,3 +1,16 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +# [2.2.0-alpha.6](/compare/@quiet/mobile@2.2.0-alpha.5...@quiet/mobile@2.2.0-alpha.6) (2024-05-16) + +**Note:** Version bump only for package @quiet/mobile + + + + + [unreleased] # New features: diff --git a/packages/mobile/android/app/build.gradle b/packages/mobile/android/app/build.gradle index 227a293444..10c2d0da0b 100644 --- a/packages/mobile/android/app/build.gradle +++ b/packages/mobile/android/app/build.gradle @@ -192,8 +192,8 @@ android { applicationId "com.quietmobile" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 417 - versionName "2.2.0-alpha.5" + versionCode 418 + versionName "2.2.0-alpha.6" resValue "string", "build_config_package", "com.quietmobile" testBuildType System.getProperty('testBuildType', 'debug') testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' diff --git a/packages/mobile/ios/Quiet/Info.plist b/packages/mobile/ios/Quiet/Info.plist index 26e639709b..920e5dc8ec 100644 --- a/packages/mobile/ios/Quiet/Info.plist +++ b/packages/mobile/ios/Quiet/Info.plist @@ -34,7 +34,7 @@ CFBundleVersion - 374 + 375 ITSAppUsesNonExemptEncryption LSRequiresIPhoneOS diff --git a/packages/mobile/ios/QuietTests/Info.plist b/packages/mobile/ios/QuietTests/Info.plist index d20d84e9ff..552df7f1d3 100644 --- a/packages/mobile/ios/QuietTests/Info.plist +++ b/packages/mobile/ios/QuietTests/Info.plist @@ -19,6 +19,6 @@ CFBundleSignature ???? CFBundleVersion - 374 + 375 diff --git a/packages/mobile/package-lock.json b/packages/mobile/package-lock.json index b443948b8d..5ed74591b3 100644 --- a/packages/mobile/package-lock.json +++ b/packages/mobile/package-lock.json @@ -1,12 +1,12 @@ { "name": "@quiet/mobile", - "version": "2.2.0-alpha.5", + "version": "2.2.0-alpha.6", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@quiet/mobile", - "version": "2.2.0-alpha.5", + "version": "2.2.0-alpha.6", "dependencies": { "@peculiar/webcrypto": "^1.4.3", "@react-native-clipboard/clipboard": "^1.13.2", diff --git a/packages/mobile/package.json b/packages/mobile/package.json index 04b3347d2c..db779d392e 100644 --- a/packages/mobile/package.json +++ b/packages/mobile/package.json @@ -1,6 +1,6 @@ { "name": "@quiet/mobile", - "version": "2.2.0-alpha.5", + "version": "2.2.0-alpha.6", "scripts": { "build": "tsc -p tsconfig.build.json --noEmit", "storybook-android": "ENVFILE=.env.storybook react-native run-android --mode=storybookDebug --appIdSuffix=storybook.debug", From d20f2b6f1dade5c582afd6f38006431e8911293e Mon Sep 17 00:00:00 2001 From: Lucas Leblow Date: Thu, 16 May 2024 12:23:13 -0600 Subject: [PATCH 38/48] Update packages CHANGELOG.md --- packages/desktop/CHANGELOG.md | 13 ------------- packages/mobile/CHANGELOG.md | 13 ------------- 2 files changed, 26 deletions(-) diff --git a/packages/desktop/CHANGELOG.md b/packages/desktop/CHANGELOG.md index e768391b6c..36baaed49a 100644 --- a/packages/desktop/CHANGELOG.md +++ b/packages/desktop/CHANGELOG.md @@ -1,16 +1,3 @@ -# Change Log - -All notable changes to this project will be documented in this file. -See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - -# [2.2.0-alpha.6](/compare/@quiet/desktop@2.2.0-alpha.5...@quiet/desktop@2.2.0-alpha.6) (2024-05-16) - -**Note:** Version bump only for package @quiet/desktop - - - - - [unreleased] # New features: diff --git a/packages/mobile/CHANGELOG.md b/packages/mobile/CHANGELOG.md index d362e9f862..36baaed49a 100644 --- a/packages/mobile/CHANGELOG.md +++ b/packages/mobile/CHANGELOG.md @@ -1,16 +1,3 @@ -# Change Log - -All notable changes to this project will be documented in this file. -See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - -# [2.2.0-alpha.6](/compare/@quiet/mobile@2.2.0-alpha.5...@quiet/mobile@2.2.0-alpha.6) (2024-05-16) - -**Note:** Version bump only for package @quiet/mobile - - - - - [unreleased] # New features: From 40b0c4d6a09ab6e7ea49fcddc8605cdce5420d64 Mon Sep 17 00:00:00 2001 From: Isla Koenigsknecht Date: Fri, 17 May 2024 14:20:00 -0400 Subject: [PATCH 39/48] Wait to dial peers until tor is fully connected, attempt to deduplicate queue, don't dial if the peer is in the peerstore --- .../connections-manager.service.ts | 41 +++++++++++++-- .../backend/src/nest/libp2p/libp2p.service.ts | 40 ++++++++++---- .../backend/src/nest/libp2p/libp2p.types.ts | 1 + .../nest/libp2p/process-in-chunks.service.ts | 52 +++++++++++++++---- .../src/nest/libp2p/process-in-chunks.spec.ts | 22 ++++++-- packages/backend/src/nest/tor/tor.service.ts | 21 +++++--- packages/mobile/ios/Quiet/Info.plist | 16 +++--- packages/types/src/socket.ts | 2 +- 8 files changed, 148 insertions(+), 47 deletions(-) diff --git a/packages/backend/src/nest/connections-manager/connections-manager.service.ts b/packages/backend/src/nest/connections-manager/connections-manager.service.ts index 20ebdc3c21..ac4eeb5cb1 100644 --- a/packages/backend/src/nest/connections-manager/connections-manager.service.ts +++ b/packages/backend/src/nest/connections-manager/connections-manager.service.ts @@ -66,6 +66,7 @@ import { emitError } from '../socket/socket.errors' import { createLibp2pAddress, isPSKcodeValid } from '@quiet/common' import { CertFieldsTypes, createRootCA, getCertFieldValue, loadCertificate } from '@quiet/identity' import { DateTime } from 'luxon' +import { platform } from 'os' @Injectable() export class ConnectionsManagerService extends EventEmitter implements OnModuleInit { @@ -75,6 +76,7 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI private ports: GetPorts isTorInit: TorInitState = TorInitState.NOT_STARTED private peerInfo: Libp2pPeerInfo | undefined = undefined + private initializationInterval: NodeJS.Timer private readonly logger = Logger(ConnectionsManagerService.name) constructor( @@ -93,6 +95,7 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI } async onModuleInit() { + this.logger('Initializing connection manager') process.on('unhandledRejection', error => { console.error(error) throw new Error() @@ -264,6 +267,19 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI this.logger('Resuming!') await this.openSocket() const peersToDial = await this.getPeersOnResume() + if (!this.tor.isTorServiceUsed) { + this.logger(`We aren't using the tor service in this client, checking bootstrap status in connection manager`) + this.initializationInterval = setInterval(async () => { + console.log('Checking bootstrap interval') + const bootstrapDone = await this.tor.isBootstrappingFinished() + if (bootstrapDone) { + clearInterval(this.initializationInterval) + this.logger('Bootstrapping is finished') + this.libp2pService?.resume(peersToDial) + } + }, 2500) + return + } this.libp2pService?.resume(peersToDial) } @@ -578,7 +594,8 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI peers: peers ? peers.slice(1) : [], psk: Libp2pService.generateLibp2pPSK(community.psk).fullKey, } - await this.libp2pService.createInstance(params) + const startDialImmediately = this.tor.isTorInitialized + await this.libp2pService.createInstance(params, startDialImmediately) // Libp2p event listeners this.libp2pService.on(Libp2pEvents.PEER_CONNECTED, async (payload: { peers: string[] }) => { @@ -634,6 +651,21 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI SocketActionTypes.CONNECTION_PROCESS_INFO, ConnectionProcessInfo.CONNECTING_TO_COMMUNITY ) + + if (!this.tor.isTorServiceUsed) { + this.logger(`We aren't using the tor service in this client, checking bootstrap status in connection manager`) + this.initializationInterval = setInterval(async () => { + console.log('Checking bootstrap interval') + const bootstrapDone = await this.tor.isBootstrappingFinished() + if (bootstrapDone) { + console.log(`Sending ${SocketActionTypes.TOR_INITIALIZED}`) + this.serverIoProvider.io.emit(SocketActionTypes.TOR_INITIALIZED) + console.log(`Sending ${SocketActionTypes.INITIAL_DIAL}`) + this.libp2pService?.emit(Libp2pEvents.INITIAL_DIAL) + clearInterval(this.initializationInterval) + } + }, 2500) + } } private attachTorEventsListeners() { @@ -642,10 +674,9 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI this.tor.on(SocketActionTypes.CONNECTION_PROCESS_INFO, data => { this.serverIoProvider.io.emit(SocketActionTypes.CONNECTION_PROCESS_INFO, data) }) - this.tor.on(SocketActionTypes.REDIAL_PEERS, async data => { - this.logger(`Socket - ${SocketActionTypes.REDIAL_PEERS}`) - const peerInfo = this.libp2pService?.getCurrentPeerInfo() - await this.libp2pService?.redialPeers([...peerInfo.connected, ...peerInfo.dialed]) + this.tor.on(SocketActionTypes.INITIAL_DIAL, async () => { + this.logger(`Socket - ${SocketActionTypes.INITIAL_DIAL}`) + this.libp2pService?.emit(Libp2pEvents.INITIAL_DIAL) }) this.socketService.on(SocketActionTypes.CONNECTION_PROCESS_INFO, data => { this.serverIoProvider.io.emit(SocketActionTypes.CONNECTION_PROCESS_INFO, data) diff --git a/packages/backend/src/nest/libp2p/libp2p.service.ts b/packages/backend/src/nest/libp2p/libp2p.service.ts index 47a90e1487..ec72a03d7d 100644 --- a/packages/backend/src/nest/libp2p/libp2p.service.ts +++ b/packages/backend/src/nest/libp2p/libp2p.service.ts @@ -41,13 +41,22 @@ export class Libp2pService extends EventEmitter { super() } - private dialPeer = async (peerAddress: string) => { - if (this.dialedPeers.has(peerAddress)) { + private dialPeer = async (peerAddress: string): Promise => { + const ma = multiaddr(peerAddress) + const peerId = peerIdFromString(ma.getPeerId()!) + + const libp2pHasPeer = await this.libp2pInstance?.peerStore.has(peerId as any) + const weHaveDialedPeer = this.dialedPeers.has(peerAddress) + + if (weHaveDialedPeer || libp2pHasPeer) { this.logger(`Skipping dial of ${peerAddress} because its already been dialed`) - return + return true } this.dialedPeers.add(peerAddress) - await this.libp2pInstance?.dial(multiaddr(peerAddress)) + const connection = await this.libp2pInstance?.dial(multiaddr(peerAddress)) + + if (connection) return true + return false } public getCurrentPeerInfo = (): Libp2pPeerInfo => { @@ -153,7 +162,7 @@ export class Libp2pService extends EventEmitter { this.processInChunksService.updateQueue(toDial) } - public async createInstance(params: Libp2pNodeParams): Promise { + public async createInstance(params: Libp2pNodeParams, startDialImmediately: boolean = false): Promise { if (this.libp2pInstance) { return this.libp2pInstance } @@ -166,7 +175,7 @@ export class Libp2pService extends EventEmitter { connectionManager: { minConnections: 3, // TODO: increase? maxConnections: 20, // TODO: increase? - dialTimeout: 120_000, + dialTimeout: 120000, maxParallelDials: 10, autoDial: true, // It's a default but let's set it to have explicit information }, @@ -205,11 +214,11 @@ export class Libp2pService extends EventEmitter { throw err } this.libp2pInstance = libp2p - await this.afterCreation(params.peers, params.peerId) + await this.afterCreation(params.peers, params.peerId, startDialImmediately) return libp2p } - private async afterCreation(peers: string[], peerId: PeerId) { + private async afterCreation(peers: string[], peerId: PeerId, startDialImmediately: boolean) { if (!this.libp2pInstance) { this.logger.error('libp2pInstance was not created') throw new Error('libp2pInstance was not created') @@ -222,9 +231,20 @@ export class Libp2pService extends EventEmitter { this.processInChunksService.updateQueue(nonDialedAddresses) }) + this.on(Libp2pEvents.INITIAL_DIAL, async () => { + this.logger('Starting initial dial') + this.processInChunksService.resume() + }) + this.logger(`Initializing libp2p for ${peerId.toString()}, bootstrapping with ${peers.length} peers`) this.serverIoProvider.io.emit(SocketActionTypes.CONNECTION_PROCESS_INFO, ConnectionProcessInfo.INITIALIZING_LIBP2P) - this.processInChunksService.init([], this.dialPeer) + + this.logger(`Initializing processInChunksService and adding ${peers.length} peers to dial initially`) + this.processInChunksService.init({ + initialData: peers, + processItem: this.dialPeer, + startImmediately: startDialImmediately, + }) this.libp2pInstance.addEventListener('peer:discovery', peer => { this.logger(`${peerId.toString()} discovered ${peer.detail.id}`) @@ -278,8 +298,6 @@ export class Libp2pService extends EventEmitter { this.emit(Libp2pEvents.PEER_DISCONNECTED, peerStat) }) - this.processInChunksService.updateQueue(peers) - this.logger(`Initialized libp2p for peer ${peerId.toString()}`) } diff --git a/packages/backend/src/nest/libp2p/libp2p.types.ts b/packages/backend/src/nest/libp2p/libp2p.types.ts index 34b7056db2..3047a3278b 100644 --- a/packages/backend/src/nest/libp2p/libp2p.types.ts +++ b/packages/backend/src/nest/libp2p/libp2p.types.ts @@ -6,6 +6,7 @@ export enum Libp2pEvents { PEER_DISCONNECTED = 'peerDisconnected', NETWORK_STATS = 'networkStats', DIAL_PEERS = 'dialPeers', + INITIAL_DIAL = 'initialDial', } export interface Libp2pNodeParams { diff --git a/packages/backend/src/nest/libp2p/process-in-chunks.service.ts b/packages/backend/src/nest/libp2p/process-in-chunks.service.ts index f83fd8d218..346284051c 100644 --- a/packages/backend/src/nest/libp2p/process-in-chunks.service.ts +++ b/packages/backend/src/nest/libp2p/process-in-chunks.service.ts @@ -2,7 +2,7 @@ import { EventEmitter } from 'events' import fastq, { queueAsPromised } from 'fastq' import Logger from '../common/logger' -import { randomUUID } from 'crypto' +import CryptoJS, { MD5 } from 'crypto-js' const DEFAULT_CHUNK_SIZE = 10 export const DEFAULT_NUM_TRIES = 2 @@ -13,24 +13,38 @@ type ProcessTask = { taskId: string } +export type ProcessInChunksServiceOptions = { + initialData: T[] + processItem: (arg: T) => Promise + chunkSize?: number | undefined + startImmediately?: boolean +} + export class ProcessInChunksService extends EventEmitter { private isActive: boolean private chunkSize: number private taskQueue: queueAsPromised> private deadLetterQueue: ProcessTask[] = [] - private processItem: (arg: T) => Promise + private processItem: (arg: T) => Promise private readonly logger = Logger(ProcessInChunksService.name) constructor() { super() } - public init(data: T[], processItem: (arg: T) => Promise, chunkSize: number = DEFAULT_CHUNK_SIZE) { - this.logger(`Initializing process-in-chunks.service with peers ${JSON.stringify(data, null, 2)}`) - this.processItem = processItem - this.chunkSize = chunkSize + public init(options: ProcessInChunksServiceOptions) { + this.logger(`Initializing process-in-chunks.service with peers ${JSON.stringify(options.initialData, null, 2)}`) + this.processItem = options.processItem + this.chunkSize = options.chunkSize ?? DEFAULT_CHUNK_SIZE this.taskQueue = fastq.promise(this, this.processOneItem, this.chunkSize) - this.isActive = true - this.updateQueue(data) + const startImmediately = options.startImmediately ?? true + if (startImmediately) { + this.logger(`Starting processing immediately`) + this.isActive = true + } else { + this.logger(`Deferring processing`) + this.pause() + } + this.updateQueue(options.initialData) } public updateQueue(items: T[]) { @@ -51,13 +65,19 @@ export class ProcessInChunksService extends EventEmitter { task = itemOrTask as ProcessTask } else { this.logger(`Creating new task for ${itemOrTask}`) - task = { data: itemOrTask as T, tries: 0, taskId: randomUUID() } + task = { data: itemOrTask as T, tries: 0, taskId: this.generateTaskId(itemOrTask as T) } } if (!this.isActive) { this.logger( 'ProcessInChunksService is not active, adding tasks to the dead letter queue!\n\nWARNING: You must call "resume" on the ProcessInChunksService to process the dead letter queue!!!' ) + if (this.deadLetterQueue.find(thisTask => thisTask.taskId === task.taskId)) { + this.logger( + `Skipping task with ID ${task.taskId} because there is another task with the same ID already in the dead letter queue.` + ) + return + } this.deadLetterQueue.push(task) this.logger(`There are now ${this.deadLetterQueue.length} items in the dead letter queue`) return @@ -79,8 +99,7 @@ export class ProcessInChunksService extends EventEmitter { let success: boolean = false try { this.logger(`Processing task ${task.taskId} with data ${task.data}`) - await this.processItem(task.data) - success = true + success = await this.processItem(task.data) } catch (e) { this.logger.error(`Processing task ${task.taskId} with data ${task.data} failed`, e) } finally { @@ -90,6 +109,13 @@ export class ProcessInChunksService extends EventEmitter { } private async pushToQueueAndRun(task: ProcessTask): Promise { + if (this.taskQueue.getQueue().find(thisTask => thisTask.taskId === task.taskId)) { + this.logger( + `Skipping task with ID ${task.taskId} because there is another task with the same ID already in the task queue.` + ) + return true + } + this.logger( `Pushing task ${task.taskId} to queue, there will now be ${this.taskQueue.length() + 1} items in the queue` ) @@ -102,6 +128,10 @@ export class ProcessInChunksService extends EventEmitter { return success } + private generateTaskId(data: T): string { + return MD5(JSON.stringify(data)).toString(CryptoJS.enc.Hex) + } + public resume() { if (this.isActive) { this.logger('ProcessInChunksService is already active') diff --git a/packages/backend/src/nest/libp2p/process-in-chunks.spec.ts b/packages/backend/src/nest/libp2p/process-in-chunks.spec.ts index 24d05dd42f..516e28c935 100644 --- a/packages/backend/src/nest/libp2p/process-in-chunks.spec.ts +++ b/packages/backend/src/nest/libp2p/process-in-chunks.spec.ts @@ -25,7 +25,7 @@ describe('ProcessInChunks', () => { .mockRejectedValueOnce(new Error('Rejected 1')) .mockResolvedValueOnce() .mockRejectedValueOnce(new Error('Rejected 2')) - processInChunks.init(['a', 'b', 'c', 'd'], mockProcessItem) + processInChunks.init({ initialData: ['a', 'b', 'c', 'd'], processItem: mockProcessItem }) await waitForExpect(() => { expect(mockProcessItem).toBeCalledTimes(6) }) @@ -38,7 +38,7 @@ describe('ProcessInChunks', () => { }) .mockResolvedValueOnce() .mockRejectedValueOnce(new Error('Rejected 1')) - processInChunks.init(['a', 'b'], mockProcessItem) + processInChunks.init({ initialData: ['a', 'b'], processItem: mockProcessItem }) processInChunks.updateQueue(['e', 'f']) await waitForExpect(() => { expect(mockProcessItem).toBeCalledTimes(5) @@ -55,7 +55,7 @@ describe('ProcessInChunks', () => { .mockResolvedValueOnce() .mockRejectedValueOnce(new Error('Rejected 2')) const chunkSize = 2 - processInChunks.init(['a', 'b', 'c', 'd'], mockProcessItem, chunkSize) + processInChunks.init({ initialData: ['a', 'b', 'c', 'd'], processItem: mockProcessItem, chunkSize }) await sleep(10000) await waitForExpect(() => { expect(mockProcessItem).toBeCalledTimes(6) @@ -64,7 +64,7 @@ describe('ProcessInChunks', () => { it('does not process more data if stopped', async () => { const mockProcessItem = jest.fn(async () => {}) - processInChunks.init([], mockProcessItem) + processInChunks.init({ initialData: [], processItem: mockProcessItem }) processInChunks.pause() processInChunks.updateQueue(['a', 'b', 'c', 'd']) expect(mockProcessItem).not.toBeCalled() @@ -72,7 +72,7 @@ describe('ProcessInChunks', () => { it('processes tasks after resuming from pause', async () => { const mockProcessItem = jest.fn(async () => {}) - processInChunks.init([], mockProcessItem) + processInChunks.init({ initialData: [], processItem: mockProcessItem }) processInChunks.pause() processInChunks.updateQueue(['a', 'b', 'c', 'd']) processInChunks.resume() @@ -80,4 +80,16 @@ describe('ProcessInChunks', () => { expect(mockProcessItem).toBeCalledTimes(4) }) }) + + it('processes tasks when deferred', async () => { + const mockProcessItem = jest.fn(async () => {}) + processInChunks.init({ initialData: ['a', 'b', 'c', 'd'], processItem: mockProcessItem, startImmediately: false }) + await waitForExpect(() => { + expect(mockProcessItem).toBeCalledTimes(0) + }) + processInChunks.resume() + await waitForExpect(() => { + expect(mockProcessItem).toBeCalledTimes(4) + }) + }) }) diff --git a/packages/backend/src/nest/tor/tor.service.ts b/packages/backend/src/nest/tor/tor.service.ts index 6b739f0572..7dcd7ccd10 100644 --- a/packages/backend/src/nest/tor/tor.service.ts +++ b/packages/backend/src/nest/tor/tor.service.ts @@ -22,9 +22,12 @@ export class Tor extends EventEmitter implements OnModuleInit { controlPort: number | undefined interval: any initTimeout: any + public isTorInitialized: boolean = false + public isTorServiceUsed: boolean = false private readonly logger = Logger(Tor.name) private hiddenServices: Map = new Map() private initializedHiddenServices: Map = new Map() + constructor( @Inject(CONFIG_OPTIONS) public configOptions: ConfigOptions, @Inject(QUIET_DIR) public readonly quietDir: string, @@ -36,11 +39,17 @@ export class Tor extends EventEmitter implements OnModuleInit { super() this.controlPort = configOptions.torControlPort - console.log('QUIET DIR', this.quietDir) + this.logger('Created tor service') + this.logger('QUIET DIR', this.quietDir) } async onModuleInit() { - if (!this.torParamsProvider.torPath) return + this.logger('Running onModuleInit in tor.service') + if (!this.torParamsProvider.torPath) { + console.warn('No tor binary path, not running the tor service') + return + } + this.isTorServiceUsed = true await this.init() } @@ -59,7 +68,7 @@ export class Tor extends EventEmitter implements OnModuleInit { return Array.from(Object.entries(this.extraTorProcessParams)).flat() } - private async isBootstrappingFinished(): Promise { + public async isBootstrappingFinished(): Promise { this.logger('Checking bootstrap status') const output = await this.torControl.sendCommand('GETINFO status/bootstrap-phase') if (output.messages[0] === '250-status/bootstrap-phase=NOTICE BOOTSTRAP PROGRESS=100 TAG=done SUMMARY="Done"') { @@ -119,11 +128,11 @@ export class Tor extends EventEmitter implements OnModuleInit { this.logger('Checking bootstrap interval') const bootstrapDone = await this.isBootstrappingFinished() if (bootstrapDone) { + this.isTorInitialized = true this.logger(`Sending ${SocketActionTypes.TOR_INITIALIZED}`) this.serverIoProvider.io.emit(SocketActionTypes.TOR_INITIALIZED) - // TODO: Figure out how to get redialing (or, ideally, initial dialing) on tor initialization working - // this.logger('Attempting to redial peers (if possible)') - // this.emit(SocketActionTypes.REDIAL_PEERS) + this.logger(`Sending ${SocketActionTypes.INITIAL_DIAL}`) + this.emit(SocketActionTypes.INITIAL_DIAL) clearInterval(this.interval) } }, 2500) diff --git a/packages/mobile/ios/Quiet/Info.plist b/packages/mobile/ios/Quiet/Info.plist index 920e5dc8ec..36b5c3c180 100644 --- a/packages/mobile/ios/Quiet/Info.plist +++ b/packages/mobile/ios/Quiet/Info.plist @@ -36,26 +36,26 @@ CFBundleVersion 375 ITSAppUsesNonExemptEncryption - + LSRequiresIPhoneOS - + NSAppTransportSecurity NSAllowsArbitraryLoads - + NSAllowsLocalNetworking - + NSExceptionDomains localhost NSExceptionAllowsInsecureHTTPLoads - + NSLocationWhenInUseUsageDescription - + UIAppFonts Rubik-Black.ttf @@ -74,7 +74,7 @@ Rubik-SemiBoldItalic.ttf UIBackgroundModes - + UILaunchStoryboardName LaunchScreen UIRequiredDeviceCapabilities @@ -88,6 +88,6 @@ UIInterfaceOrientationLandscapeRight UIViewControllerBasedStatusBarAppearance - + diff --git a/packages/types/src/socket.ts b/packages/types/src/socket.ts index 4fcb44cfdb..07a8b77e1d 100644 --- a/packages/types/src/socket.ts +++ b/packages/types/src/socket.ts @@ -71,7 +71,7 @@ export enum SocketActionTypes { PEER_CONNECTED = 'peerConnected', PEER_DISCONNECTED = 'peerDisconnected', TOR_INITIALIZED = 'torInitialized', - REDIAL_PEERS = 'redialPeers', + INITIAL_DIAL = 'initialDial', // ====== Misc ====== From 4fe11c477be1574ec04d0f4040afba96513b2945 Mon Sep 17 00:00:00 2001 From: Isla Koenigsknecht Date: Fri, 17 May 2024 15:02:32 -0400 Subject: [PATCH 40/48] Better deduplication --- .../nest/libp2p/process-in-chunks.service.ts | 43 +++++++++++++------ 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/packages/backend/src/nest/libp2p/process-in-chunks.service.ts b/packages/backend/src/nest/libp2p/process-in-chunks.service.ts index 346284051c..62cbd47d12 100644 --- a/packages/backend/src/nest/libp2p/process-in-chunks.service.ts +++ b/packages/backend/src/nest/libp2p/process-in-chunks.service.ts @@ -25,6 +25,7 @@ export class ProcessInChunksService extends EventEmitter { private chunkSize: number private taskQueue: queueAsPromised> private deadLetterQueue: ProcessTask[] = [] + private runningTaskIds: Set = new Set() private processItem: (arg: T) => Promise private readonly logger = Logger(ProcessInChunksService.name) constructor() { @@ -68,16 +69,14 @@ export class ProcessInChunksService extends EventEmitter { task = { data: itemOrTask as T, tries: 0, taskId: this.generateTaskId(itemOrTask as T) } } + if (this.isTaskDuplicate(task.taskId)) { + return + } + if (!this.isActive) { this.logger( 'ProcessInChunksService is not active, adding tasks to the dead letter queue!\n\nWARNING: You must call "resume" on the ProcessInChunksService to process the dead letter queue!!!' ) - if (this.deadLetterQueue.find(thisTask => thisTask.taskId === task.taskId)) { - this.logger( - `Skipping task with ID ${task.taskId} because there is another task with the same ID already in the dead letter queue.` - ) - return - } this.deadLetterQueue.push(task) this.logger(`There are now ${this.deadLetterQueue.length} items in the dead letter queue`) return @@ -109,17 +108,12 @@ export class ProcessInChunksService extends EventEmitter { } private async pushToQueueAndRun(task: ProcessTask): Promise { - if (this.taskQueue.getQueue().find(thisTask => thisTask.taskId === task.taskId)) { - this.logger( - `Skipping task with ID ${task.taskId} because there is another task with the same ID already in the task queue.` - ) - return true - } - this.logger( `Pushing task ${task.taskId} to queue, there will now be ${this.taskQueue.length() + 1} items in the queue` ) + this.runningTaskIds.add(task.taskId) const success = await this.taskQueue.push(task) + this.runningTaskIds.delete(task.taskId) if (success) { this.logger(`Task ${task.taskId} completed successfully`) } else { @@ -128,6 +122,29 @@ export class ProcessInChunksService extends EventEmitter { return success } + private isTaskDuplicate(taskId: string): boolean { + if (!this.isActive) { + this.logger( + 'ProcessInChunksService is not active, adding tasks to the dead letter queue!\n\nWARNING: You must call "resume" on the ProcessInChunksService to process the dead letter queue!!!' + ) + return this.deadLetterQueue.find(thisTask => thisTask.taskId === taskId) != null + } + + if (this.runningTaskIds.has(taskId)) { + this.logger(`Skipping task with ID ${taskId} because there is another task with the same ID currently running.`) + return true + } + + if (this.taskQueue.getQueue().find(thisTask => thisTask.taskId === taskId)) { + this.logger( + `Skipping task with ID ${taskId} because there is another task with the same ID already in the task queue.` + ) + return true + } + + return false + } + private generateTaskId(data: T): string { return MD5(JSON.stringify(data)).toString(CryptoJS.enc.Hex) } From b46934eb4e17af2ff1c1f7108427c0131ec84369 Mon Sep 17 00:00:00 2001 From: Isla Koenigsknecht Date: Fri, 17 May 2024 15:03:12 -0400 Subject: [PATCH 41/48] Add packages that error out on bootstrap --- packages/backend/package-lock.json | 90 ++++++++++++++++++++++++++++++ packages/backend/package.json | 2 + 2 files changed, 92 insertions(+) diff --git a/packages/backend/package-lock.json b/packages/backend/package-lock.json index 2697f66df2..2700dbf028 100644 --- a/packages/backend/package-lock.json +++ b/packages/backend/package-lock.json @@ -16,6 +16,7 @@ "@nestjs/platform-express": "^10.2.10", "@peculiar/webcrypto": "1.4.3", "abortable-iterator": "^3.0.0", + "bufferutil": "^4.0.8", "class-transformer": "^0.5.1", "class-validator": "^0.13.1", "cli-table": "^0.3.6", @@ -57,6 +58,7 @@ "socks-proxy-agent": "^5.0.0", "string-replace-loader": "3.1.0", "ts-jest-resolver": "^2.0.0", + "utf-8-validate": "^6.0.4", "validator": "^13.11.0" }, "devDependencies": { @@ -1513,6 +1515,20 @@ "ws": "7.4.6" } }, + "node_modules/@ethersproject/providers/node_modules/utf-8-validate": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", + "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==", + "hasInstallScript": true, + "optional": true, + "peer": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, "node_modules/@ethersproject/providers/node_modules/ws": { "version": "7.4.6", "license": "MIT", @@ -8743,6 +8759,18 @@ "version": "1.0.3", "license": "MIT" }, + "node_modules/bufferutil": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.8.tgz", + "integrity": "sha512-4T53u4PdgsXqKaIctwF8ifXlRTTmEPJ8iEPWFdGZvcf7sbwYo6FKFEX9eNNAnzFZ7EzJAQ3CJeOtCRA4rDp7Pw==", + "hasInstallScript": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, "node_modules/busboy": { "version": "1.6.0", "dependencies": { @@ -10373,6 +10401,20 @@ "xmlhttprequest-ssl": "~2.0.0" } }, + "node_modules/engine.io-client/node_modules/utf-8-validate": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", + "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==", + "hasInstallScript": true, + "optional": true, + "peer": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, "node_modules/engine.io-client/node_modules/ws": { "version": "8.2.3", "license": "MIT", @@ -22890,6 +22932,18 @@ "node": ">=4" } }, + "node_modules/utf-8-validate": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-6.0.4.tgz", + "integrity": "sha512-xu9GQDeFp+eZ6LnCywXN/zBancWvOpUMzgjLPSjy4BRHSmTelvn2E0DG0o1sTiw5hkCKBHo8rwSKncfRfv2EEQ==", + "hasInstallScript": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, "node_modules/utf8-byte-length": { "version": "1.0.4", "license": "WTFPL" @@ -24434,6 +24488,16 @@ "ws": "7.4.6" }, "dependencies": { + "utf-8-validate": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", + "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==", + "optional": true, + "peer": true, + "requires": { + "node-gyp-build": "^4.3.0" + } + }, "ws": { "version": "7.4.6", "requires": {} @@ -29428,6 +29492,14 @@ "buffer-xor": { "version": "1.0.3" }, + "bufferutil": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.8.tgz", + "integrity": "sha512-4T53u4PdgsXqKaIctwF8ifXlRTTmEPJ8iEPWFdGZvcf7sbwYo6FKFEX9eNNAnzFZ7EzJAQ3CJeOtCRA4rDp7Pw==", + "requires": { + "node-gyp-build": "^4.3.0" + } + }, "busboy": { "version": "1.6.0", "requires": { @@ -30512,6 +30584,16 @@ "xmlhttprequest-ssl": "~2.0.0" }, "dependencies": { + "utf-8-validate": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", + "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==", + "optional": true, + "peer": true, + "requires": { + "node-gyp-build": "^4.3.0" + } + }, "ws": { "version": "8.2.3", "requires": {} @@ -38336,6 +38418,14 @@ "nan": "^2.14.2" } }, + "utf-8-validate": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-6.0.4.tgz", + "integrity": "sha512-xu9GQDeFp+eZ6LnCywXN/zBancWvOpUMzgjLPSjy4BRHSmTelvn2E0DG0o1sTiw5hkCKBHo8rwSKncfRfv2EEQ==", + "requires": { + "node-gyp-build": "^4.3.0" + } + }, "utf8-byte-length": { "version": "1.0.4" }, diff --git a/packages/backend/package.json b/packages/backend/package.json index e663a57080..6cee5e72c3 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -98,6 +98,7 @@ "@quiet/logger": "^2.0.2-alpha.0", "@quiet/types": "^2.0.2-alpha.1", "abortable-iterator": "^3.0.0", + "bufferutil": "^4.0.8", "class-transformer": "^0.5.1", "class-validator": "^0.13.1", "cli-table": "^0.3.6", @@ -139,6 +140,7 @@ "socks-proxy-agent": "^5.0.0", "string-replace-loader": "3.1.0", "ts-jest-resolver": "^2.0.0", + "utf-8-validate": "^6.0.4", "validator": "^13.11.0" }, "overrides": { From f5c63b467b1845e2da46b7420b260276da70a441 Mon Sep 17 00:00:00 2001 From: Isla Koenigsknecht Date: Fri, 17 May 2024 15:25:30 -0400 Subject: [PATCH 42/48] Add extra libp2p logging --- packages/desktop/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/desktop/package.json b/packages/desktop/package.json index a1b16734cc..07f86bae94 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -116,7 +116,7 @@ "build:renderer:prod": "webpack --config webpack/webpack.config.renderer.prod.js", "postBuild": "node scripts/postBuild.js", "prestart": "npm run build:main", - "start": "cross-env DEBUG='backend*,quiet*,state-manager*,desktop*,utils*,libp2p:websockets:listener:backend,libp2p:connection-manager:auto-dialler' npm run start:renderer", + "start": "cross-env DEBUG='backend*,quiet*,state-manager*,desktop*,utils*,libp2p:websockets:listener:backend,libp2p:connection-manager:auto-dialler,libp2p:pnet' npm run start:renderer", "start:main": "cross-env NODE_ENV=development electron .", "start:renderer": "cross-env NODE_ENV=development webpack-dev-server --config webpack/webpack.config.renderer.dev.js", "storybook": "export NODE_OPTIONS=--openssl-legacy-provider && start-storybook -p 6006", From c726a9cc2bd84c4bf9efc03ce49bc33c6a8332b1 Mon Sep 17 00:00:00 2001 From: Isla Koenigsknecht Date: Fri, 17 May 2024 15:27:42 -0400 Subject: [PATCH 43/48] Update max dials and tie together chunksize and maxparalleldials --- .../backend/src/nest/libp2p/libp2p.service.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/packages/backend/src/nest/libp2p/libp2p.service.ts b/packages/backend/src/nest/libp2p/libp2p.service.ts index ec72a03d7d..788d349f81 100644 --- a/packages/backend/src/nest/libp2p/libp2p.service.ts +++ b/packages/backend/src/nest/libp2p/libp2p.service.ts @@ -168,15 +168,16 @@ export class Libp2pService extends EventEmitter { } let libp2p: Libp2p + const maxParallelDials = 2 try { libp2p = await createLibp2p({ start: false, connectionManager: { - minConnections: 3, // TODO: increase? + minConnections: 5, // TODO: increase? maxConnections: 20, // TODO: increase? dialTimeout: 120000, - maxParallelDials: 10, + maxParallelDials, autoDial: true, // It's a default but let's set it to have explicit information }, peerId: params.peerId, @@ -214,11 +215,16 @@ export class Libp2pService extends EventEmitter { throw err } this.libp2pInstance = libp2p - await this.afterCreation(params.peers, params.peerId, startDialImmediately) + await this.afterCreation(params.peers, params.peerId, maxParallelDials, startDialImmediately) return libp2p } - private async afterCreation(peers: string[], peerId: PeerId, startDialImmediately: boolean) { + private async afterCreation( + peers: string[], + peerId: PeerId, + maxParallelDials: number, + startDialImmediately: boolean + ) { if (!this.libp2pInstance) { this.logger.error('libp2pInstance was not created') throw new Error('libp2pInstance was not created') @@ -244,6 +250,7 @@ export class Libp2pService extends EventEmitter { initialData: peers, processItem: this.dialPeer, startImmediately: startDialImmediately, + chunkSize: maxParallelDials, }) this.libp2pInstance.addEventListener('peer:discovery', peer => { From 7f1bce6550a1a6ea947f63ba98cfdb81e7ec88a3 Mon Sep 17 00:00:00 2001 From: Isla Koenigsknecht Date: Fri, 17 May 2024 16:30:18 -0400 Subject: [PATCH 44/48] More logging and improvements to connections --- packages/backend/src/nest/app.module.ts | 5 +++-- packages/backend/src/nest/libp2p/libp2p.service.ts | 5 ++++- .../backend/src/nest/websocketOverTor/index.ts | 14 ++++++++++++-- packages/desktop/package.json | 2 +- packages/mobile/ios/NodeJsMobile/NodeRunner.mm | 2 +- 5 files changed, 21 insertions(+), 7 deletions(-) diff --git a/packages/backend/src/nest/app.module.ts b/packages/backend/src/nest/app.module.ts index 01b4215930..e575fb302f 100644 --- a/packages/backend/src/nest/app.module.ts +++ b/packages/backend/src/nest/app.module.ts @@ -99,8 +99,9 @@ export class AppModule { allowedHeaders: ['authorization'], credentials: true, }, - pingInterval: 1000_000, - pingTimeout: 1000_000, + pingInterval: 10_000, + pingTimeout: 2_000, + connectTimeout: 60_000, }) io.engine.use((req, res, next) => { const authHeader = req.headers['authorization'] diff --git a/packages/backend/src/nest/libp2p/libp2p.service.ts b/packages/backend/src/nest/libp2p/libp2p.service.ts index 788d349f81..5b552217e6 100644 --- a/packages/backend/src/nest/libp2p/libp2p.service.ts +++ b/packages/backend/src/nest/libp2p/libp2p.service.ts @@ -208,7 +208,10 @@ export class Libp2pService extends EventEmitter { }), ], dht: kadDHT(), - pubsub: gossipsub({ allowPublishToZeroPeers: true }), + pubsub: gossipsub({ + allowPublishToZeroPeers: true, + doPX: true, + }), }) } catch (err) { this.logger.error('Create libp2p:', err) diff --git a/packages/backend/src/nest/websocketOverTor/index.ts b/packages/backend/src/nest/websocketOverTor/index.ts index c0b1160214..ef17b19e79 100644 --- a/packages/backend/src/nest/websocketOverTor/index.ts +++ b/packages/backend/src/nest/websocketOverTor/index.ts @@ -18,6 +18,7 @@ import { type ServerOptions, type WebSocketServer as ItWsWebsocketServer } from import { multiaddr } from '@multiformats/multiaddr' import { type MultiaddrConnection, type Connection } from '@libp2p/interface-connection' import logger from '../common/logger' +import { DuplexWebSocket } from 'it-ws/dist/src/duplex' const log = logger('libp2p:websockets') @@ -73,10 +74,11 @@ export class WebSockets extends EventEmitter { async dial(ma: Multiaddr, options: DialOptions) { let conn: Connection - let socket + let socket: DuplexWebSocket let maConn: MultiaddrConnection try { + log(`Connecting socket with ${ma.toString()}`) socket = await this._connect(ma, { websocket: { ...this._websocketOpts, @@ -88,6 +90,7 @@ export class WebSockets extends EventEmitter { throw e } try { + log(`Creating multiaddr connection from socket with ${ma.toString()}`) maConn = socketToMaConn(socket, ma, { signal: options.signal }) log('new outbound connection %s', maConn.remoteAddr) } catch (e) { @@ -96,6 +99,7 @@ export class WebSockets extends EventEmitter { } try { + log(`Upgrading outbound connection with ${maConn.remoteAddr.toString()}`) conn = await options.upgrader.upgradeOutbound(maConn) log('outbound connection %s upgraded', maConn.remoteAddr) return conn @@ -120,6 +124,7 @@ export class WebSockets extends EventEmitter { const myUri = `${toUri(ma)}/?remoteAddress=${encodeURIComponent(this.localAddress)}` + log(`Creating raw socket connection to ${ma.toString()}`) const rawSocket = connect(myUri, Object.assign({ binary: true }, options)) if (rawSocket.socket.on) { @@ -129,6 +134,7 @@ export class WebSockets extends EventEmitter { } if (!options.signal) { + log(`Waiting for socket connection to ${ma.toString()} with no abort signal`) await Promise.race([rawSocket.connected(), errorPromise.promise]) log(`${this.localAddress} connected %s`, ma) @@ -155,6 +161,7 @@ export class WebSockets extends EventEmitter { }) try { + log(`Waiting for socket connection to ${ma.toString()}`) await Promise.race([abort, errorPromise.promise, rawSocket.connected()]) } finally { options.signal.removeEventListener('abort', onAbort) @@ -200,8 +207,10 @@ export class WebSockets extends EventEmitter { if (!query.remoteAddress) return const remoteAddress = query.remoteAddress.toString() + const ma = multiaddr(remoteAddress) try { - maConn = socketToMaConn(stream, multiaddr(remoteAddress)) + log(`Creating multiaddr connection for inbound peer ${ma.toString()}`) + maConn = socketToMaConn(stream, ma) const peer = { id: PeerId.createFromB58String(remoteAddress.split('/p2p/')[1]), multiaddrs: [maConn.remoteAddr], @@ -214,6 +223,7 @@ export class WebSockets extends EventEmitter { } try { + log(`Upgrading inbound connection with ${maConn.remoteAddr.toString()}`) conn = await upgrader.upgradeInbound(maConn) } catch (err) { log.error('inbound connection failed to upgrade', err) diff --git a/packages/desktop/package.json b/packages/desktop/package.json index 07f86bae94..d475529588 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -116,7 +116,7 @@ "build:renderer:prod": "webpack --config webpack/webpack.config.renderer.prod.js", "postBuild": "node scripts/postBuild.js", "prestart": "npm run build:main", - "start": "cross-env DEBUG='backend*,quiet*,state-manager*,desktop*,utils*,libp2p:websockets:listener:backend,libp2p:connection-manager:auto-dialler,libp2p:pnet' npm run start:renderer", + "start": "cross-env DEBUG='backend*,quiet*,state-manager*,desktop*,utils*,libp2p:websockets:listener:backend,libp2p:connection-manager:auto-dialler,libp2p:pnet,libp2p:upgrader' npm run start:renderer", "start:main": "cross-env NODE_ENV=development electron .", "start:renderer": "cross-env NODE_ENV=development webpack-dev-server --config webpack/webpack.config.renderer.dev.js", "storybook": "export NODE_OPTIONS=--openssl-legacy-provider && start-storybook -p 6006", diff --git a/packages/mobile/ios/NodeJsMobile/NodeRunner.mm b/packages/mobile/ios/NodeJsMobile/NodeRunner.mm index 35f909e793..8ee169bbf9 100644 --- a/packages/mobile/ios/NodeJsMobile/NodeRunner.mm +++ b/packages/mobile/ios/NodeJsMobile/NodeRunner.mm @@ -205,7 +205,7 @@ - (void) startEngineWithArguments:(NSArray*)arguments:(NSString*)builtinModulesP nodePath = [nodePath stringByAppendingString:builtinModulesPath]; } setenv([@"NODE_PATH" UTF8String], (const char*)[nodePath UTF8String], 1); - setenv([@"DEBUG" UTF8String], "backend:*,state-manager:*,libp2p:pnet", 1); + setenv([@"DEBUG" UTF8String], "backend:*,state-manager:*,libp2p:websockets:listener:backend,libp2p:connection-manager:auto-dialler,libp2p:pnet,libp2p:upgrader", 1); int c_arguments_size=0; From 75818c8ac5948dc46edce5ac0a9a38970d95ff5c Mon Sep 17 00:00:00 2001 From: Isla Koenigsknecht Date: Fri, 17 May 2024 16:45:07 -0400 Subject: [PATCH 45/48] Code consolidation --- .../connections-manager.service.ts | 51 ++++++++++--------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/packages/backend/src/nest/connections-manager/connections-manager.service.ts b/packages/backend/src/nest/connections-manager/connections-manager.service.ts index ac4eeb5cb1..aa9d5b985b 100644 --- a/packages/backend/src/nest/connections-manager/connections-manager.service.ts +++ b/packages/backend/src/nest/connections-manager/connections-manager.service.ts @@ -267,19 +267,11 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI this.logger('Resuming!') await this.openSocket() const peersToDial = await this.getPeersOnResume() - if (!this.tor.isTorServiceUsed) { - this.logger(`We aren't using the tor service in this client, checking bootstrap status in connection manager`) - this.initializationInterval = setInterval(async () => { - console.log('Checking bootstrap interval') - const bootstrapDone = await this.tor.isBootstrappingFinished() - if (bootstrapDone) { - clearInterval(this.initializationInterval) - this.logger('Bootstrapping is finished') - this.libp2pService?.resume(peersToDial) - } - }, 2500) - return + const callback = async () => { + this.logger('Bootstrapping is finished') + this.libp2pService?.resume(peersToDial) } + if (await this.runOnTorBootstrap(callback)) return this.libp2pService?.resume(peersToDial) } @@ -652,20 +644,13 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI ConnectionProcessInfo.CONNECTING_TO_COMMUNITY ) - if (!this.tor.isTorServiceUsed) { - this.logger(`We aren't using the tor service in this client, checking bootstrap status in connection manager`) - this.initializationInterval = setInterval(async () => { - console.log('Checking bootstrap interval') - const bootstrapDone = await this.tor.isBootstrappingFinished() - if (bootstrapDone) { - console.log(`Sending ${SocketActionTypes.TOR_INITIALIZED}`) - this.serverIoProvider.io.emit(SocketActionTypes.TOR_INITIALIZED) - console.log(`Sending ${SocketActionTypes.INITIAL_DIAL}`) - this.libp2pService?.emit(Libp2pEvents.INITIAL_DIAL) - clearInterval(this.initializationInterval) - } - }, 2500) + const callback = async () => { + console.log(`Sending ${SocketActionTypes.TOR_INITIALIZED}`) + this.serverIoProvider.io.emit(SocketActionTypes.TOR_INITIALIZED) + console.log(`Sending ${SocketActionTypes.INITIAL_DIAL}`) + this.libp2pService?.emit(Libp2pEvents.INITIAL_DIAL) } + await this.runOnTorBootstrap(callback) } private attachTorEventsListeners() { @@ -871,4 +856,20 @@ export class ConnectionsManagerService extends EventEmitter implements OnModuleI this.serverIoProvider.io.emit(SocketActionTypes.USER_PROFILES_STORED, payload) }) } + + private async runOnTorBootstrap(callback: () => Promise, intervalTimerMs: number = 2500): Promise { + if (!this.tor.isTorServiceUsed) { + this.logger(`We aren't using the tor service in this client, checking bootstrap status in connection manager`) + this.initializationInterval = setInterval(async () => { + console.log('Checking bootstrap interval') + const bootstrapDone = await this.tor.isBootstrappingFinished() + if (bootstrapDone) { + clearInterval(this.initializationInterval) + await callback() + } + }, intervalTimerMs) + return true + } + return false + } } From c9c78369a801345fd17ca96b73b4822a3d1978b2 Mon Sep 17 00:00:00 2001 From: Isla Koenigsknecht Date: Fri, 17 May 2024 16:52:39 -0400 Subject: [PATCH 46/48] Use CryptoJS import for MD5 --- packages/backend/src/nest/libp2p/process-in-chunks.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/backend/src/nest/libp2p/process-in-chunks.service.ts b/packages/backend/src/nest/libp2p/process-in-chunks.service.ts index 62cbd47d12..c55b131818 100644 --- a/packages/backend/src/nest/libp2p/process-in-chunks.service.ts +++ b/packages/backend/src/nest/libp2p/process-in-chunks.service.ts @@ -2,7 +2,7 @@ import { EventEmitter } from 'events' import fastq, { queueAsPromised } from 'fastq' import Logger from '../common/logger' -import CryptoJS, { MD5 } from 'crypto-js' +import CryptoJS from 'crypto-js' const DEFAULT_CHUNK_SIZE = 10 export const DEFAULT_NUM_TRIES = 2 @@ -146,7 +146,7 @@ export class ProcessInChunksService extends EventEmitter { } private generateTaskId(data: T): string { - return MD5(JSON.stringify(data)).toString(CryptoJS.enc.Hex) + return CryptoJS.MD5(JSON.stringify(data)).toString(CryptoJS.enc.Hex) } public resume() { From 97719b209db1b6dd191a6af440f1abeb28540cd7 Mon Sep 17 00:00:00 2001 From: Isla Koenigsknecht Date: Fri, 17 May 2024 17:55:36 -0400 Subject: [PATCH 47/48] Fix tests --- .../src/nest/libp2p/libp2p.service.spec.ts | 31 +++++++++----- .../nest/libp2p/process-in-chunks.service.ts | 2 +- .../src/nest/libp2p/process-in-chunks.spec.ts | 40 +++++++++++++------ packages/backend/src/nest/tor/tor.service.ts | 5 +-- 4 files changed, 50 insertions(+), 28 deletions(-) diff --git a/packages/backend/src/nest/libp2p/libp2p.service.spec.ts b/packages/backend/src/nest/libp2p/libp2p.service.spec.ts index 91d9d177c4..5822a1502a 100644 --- a/packages/backend/src/nest/libp2p/libp2p.service.spec.ts +++ b/packages/backend/src/nest/libp2p/libp2p.service.spec.ts @@ -9,6 +9,7 @@ import { toString as uint8ArrayToString } from 'uint8arrays/to-string' import validator from 'validator' import waitForExpect from 'wait-for-expect' import { DEFAULT_NUM_TRIES, ProcessInChunksService } from './process-in-chunks.service' +import { sleep } from '../common/sleep' describe('Libp2pService', () => { let module: TestingModule @@ -16,7 +17,7 @@ describe('Libp2pService', () => { let params: Libp2pNodeParams let processInChunks: ProcessInChunksService - beforeAll(async () => { + beforeEach(async () => { module = await Test.createTestingModule({ imports: [TestModule, Libp2pModule], }).compile() @@ -26,7 +27,7 @@ describe('Libp2pService', () => { params = await libp2pInstanceParams() }) - afterAll(async () => { + afterEach(async () => { await libp2pService.libp2pInstance?.stop() await module.close() }) @@ -71,14 +72,19 @@ describe('Libp2pService', () => { libp2pService.createLibp2pAddress('onionAddress1.onion', peerId1.toString()), libp2pService.createLibp2pAddress('onionAddress2.onion', peerId2.toString()), ] - await libp2pService.createInstance(params) - // @ts-expect-error processItem is private - const spyOnProcessItem = jest.spyOn(processInChunks, 'processItem') + await libp2pService.createInstance(params, false) expect(libp2pService.libp2pInstance).not.toBeNull() + + // @ts-expect-error processItem is private + const processItemSpy = jest.spyOn(processInChunks, 'processItem') + const dialSpy = jest.spyOn(libp2pService.libp2pInstance!, 'dial') + libp2pService.emit(Libp2pEvents.INITIAL_DIAL, addresses) libp2pService.emit(Libp2pEvents.DIAL_PEERS, addresses) + await waitForExpect(async () => { - expect(spyOnProcessItem).toBeCalledTimes(addresses.length) - }) + expect(processItemSpy).toBeCalledTimes(6) + expect(dialSpy).toBeCalledTimes(3) + }, 30000) }) it(`Do not dial peer on '${Libp2pEvents.DIAL_PEERS}' event if peer was already dialed`, async () => { @@ -90,15 +96,18 @@ describe('Libp2pService', () => { alreadyDialedAddress, libp2pService.createLibp2pAddress('onionAddress2.onion', peerId2.toString()), ] - await libp2pService.createInstance(params) + await libp2pService.createInstance(params, false) expect(libp2pService.libp2pInstance).not.toBeNull() + // @ts-expect-error processItem is private const processItemSpy = jest.spyOn(processInChunks, 'processItem') const dialSpy = jest.spyOn(libp2pService.libp2pInstance!, 'dial') + libp2pService.emit(Libp2pEvents.INITIAL_DIAL, addresses) libp2pService.emit(Libp2pEvents.DIAL_PEERS, addresses) + await waitForExpect(async () => { - expect(processItemSpy).toBeCalledTimes(2 * DEFAULT_NUM_TRIES) - expect(dialSpy).toBeCalledTimes(1) - }) + expect(processItemSpy).toBeCalledTimes(4) + expect(dialSpy).toBeCalledTimes(2) + }, 30000) }) }) diff --git a/packages/backend/src/nest/libp2p/process-in-chunks.service.ts b/packages/backend/src/nest/libp2p/process-in-chunks.service.ts index c55b131818..97c194f176 100644 --- a/packages/backend/src/nest/libp2p/process-in-chunks.service.ts +++ b/packages/backend/src/nest/libp2p/process-in-chunks.service.ts @@ -15,7 +15,7 @@ type ProcessTask = { export type ProcessInChunksServiceOptions = { initialData: T[] - processItem: (arg: T) => Promise + processItem: (arg: T) => Promise chunkSize?: number | undefined startImmediately?: boolean } diff --git a/packages/backend/src/nest/libp2p/process-in-chunks.spec.ts b/packages/backend/src/nest/libp2p/process-in-chunks.spec.ts index 516e28c935..0075ba3371 100644 --- a/packages/backend/src/nest/libp2p/process-in-chunks.spec.ts +++ b/packages/backend/src/nest/libp2p/process-in-chunks.spec.ts @@ -20,12 +20,13 @@ describe('ProcessInChunks', () => { const mockProcessItem = jest .fn(async a => { console.log('processing', a) + return true }) - .mockResolvedValueOnce() + .mockResolvedValueOnce(true) .mockRejectedValueOnce(new Error('Rejected 1')) - .mockResolvedValueOnce() + .mockResolvedValueOnce(true) .mockRejectedValueOnce(new Error('Rejected 2')) - processInChunks.init({ initialData: ['a', 'b', 'c', 'd'], processItem: mockProcessItem }) + processInChunks.init({ initialData: ['a', 'b', 'c', 'd'], processItem: mockProcessItem, chunkSize: 10 }) await waitForExpect(() => { expect(mockProcessItem).toBeCalledTimes(6) }) @@ -35,10 +36,11 @@ describe('ProcessInChunks', () => { const mockProcessItem = jest .fn(async a => { console.log('processing', a) + return true }) - .mockResolvedValueOnce() + .mockResolvedValueOnce(true) .mockRejectedValueOnce(new Error('Rejected 1')) - processInChunks.init({ initialData: ['a', 'b'], processItem: mockProcessItem }) + processInChunks.init({ initialData: ['a', 'b'], processItem: mockProcessItem, chunkSize: 10 }) processInChunks.updateQueue(['e', 'f']) await waitForExpect(() => { expect(mockProcessItem).toBeCalledTimes(5) @@ -49,10 +51,11 @@ describe('ProcessInChunks', () => { const mockProcessItem = jest .fn(async a => { console.log('processing', a) + return true }) - .mockResolvedValueOnce() + .mockResolvedValueOnce(true) .mockRejectedValueOnce(new Error('Rejected 1')) - .mockResolvedValueOnce() + .mockResolvedValueOnce(true) .mockRejectedValueOnce(new Error('Rejected 2')) const chunkSize = 2 processInChunks.init({ initialData: ['a', 'b', 'c', 'd'], processItem: mockProcessItem, chunkSize }) @@ -63,16 +66,20 @@ describe('ProcessInChunks', () => { }) it('does not process more data if stopped', async () => { - const mockProcessItem = jest.fn(async () => {}) - processInChunks.init({ initialData: [], processItem: mockProcessItem }) + const mockProcessItem = jest.fn(async () => { + return true + }) + processInChunks.init({ initialData: [], processItem: mockProcessItem, chunkSize: 10 }) processInChunks.pause() processInChunks.updateQueue(['a', 'b', 'c', 'd']) expect(mockProcessItem).not.toBeCalled() }) it('processes tasks after resuming from pause', async () => { - const mockProcessItem = jest.fn(async () => {}) - processInChunks.init({ initialData: [], processItem: mockProcessItem }) + const mockProcessItem = jest.fn(async () => { + return true + }) + processInChunks.init({ initialData: [], processItem: mockProcessItem, chunkSize: 10 }) processInChunks.pause() processInChunks.updateQueue(['a', 'b', 'c', 'd']) processInChunks.resume() @@ -82,8 +89,15 @@ describe('ProcessInChunks', () => { }) it('processes tasks when deferred', async () => { - const mockProcessItem = jest.fn(async () => {}) - processInChunks.init({ initialData: ['a', 'b', 'c', 'd'], processItem: mockProcessItem, startImmediately: false }) + const mockProcessItem = jest.fn(async () => { + return true + }) + processInChunks.init({ + initialData: ['a', 'b', 'c', 'd'], + processItem: mockProcessItem, + startImmediately: false, + chunkSize: 10, + }) await waitForExpect(() => { expect(mockProcessItem).toBeCalledTimes(0) }) diff --git a/packages/backend/src/nest/tor/tor.service.ts b/packages/backend/src/nest/tor/tor.service.ts index 7dcd7ccd10..8bbbbb3208 100644 --- a/packages/backend/src/nest/tor/tor.service.ts +++ b/packages/backend/src/nest/tor/tor.service.ts @@ -49,7 +49,6 @@ export class Tor extends EventEmitter implements OnModuleInit { console.warn('No tor binary path, not running the tor service') return } - this.isTorServiceUsed = true await this.init() } @@ -79,6 +78,7 @@ export class Tor extends EventEmitter implements OnModuleInit { } public async init(timeout = 120_000): Promise { + this.isTorServiceUsed = true if (!this.socksPort) this.socksPort = await getPort() this.logger('Initializing tor...') @@ -134,12 +134,11 @@ export class Tor extends EventEmitter implements OnModuleInit { this.logger(`Sending ${SocketActionTypes.INITIAL_DIAL}`) this.emit(SocketActionTypes.INITIAL_DIAL) clearInterval(this.interval) + resolve() } }, 2500) this.logger(`Spawned tor with pid(s): ${this.getTorProcessIds()}`) - - resolve() } catch (e) { this.logger('Killing tor due to error', e) this.clearHangingTorProcess() From c745690ebed9e50db6507a3123294529ff27ee16 Mon Sep 17 00:00:00 2001 From: Isla Koenigsknecht Date: Thu, 23 May 2024 10:56:52 -0400 Subject: [PATCH 48/48] Tor improvements and better test fix --- .../connections-manager.service.tor.spec.ts | 10 ++++++-- .../backend/src/nest/ipfs/ipfs.service.ts | 14 +++++++---- .../backend/src/nest/libp2p/libp2p.service.ts | 24 +++++++++++++++---- packages/backend/src/nest/tor/tor.service.ts | 20 ++++++++++++---- packages/mobile/ios/TorHandler.swift | 6 +++++ 5 files changed, 60 insertions(+), 14 deletions(-) diff --git a/packages/backend/src/nest/connections-manager/connections-manager.service.tor.spec.ts b/packages/backend/src/nest/connections-manager/connections-manager.service.tor.spec.ts index 4506ed02d8..633d111339 100644 --- a/packages/backend/src/nest/connections-manager/connections-manager.service.tor.spec.ts +++ b/packages/backend/src/nest/connections-manager/connections-manager.service.tor.spec.ts @@ -208,13 +208,19 @@ describe('Connections manager', () => { hiddenService: userIdentity.hiddenService, }, } + const emitSpy = jest.spyOn(libp2pService, 'emit') await connectionsManagerService.init() await connectionsManagerService.launchCommunity(launchCommunityPayload) - await sleep(5000) + await waitForExpect(async () => { + expect(emitSpy).toHaveBeenCalledWith(Libp2pEvents.INITIAL_DIAL) + }, 30000) + // It looks LibP2P dials peers initially when it's started and // then IPFS service dials peers again when started, thus // peersCount-1 * 2 because we don't dial ourself (the first peer in the list) - expect(spyOnDial).toHaveBeenCalledTimes((peersCount - 1) * 2) + await waitForExpect(async () => { + expect(spyOnDial).toHaveBeenCalledTimes((peersCount - 1) * 2) + }, 45000) // Temporary fix for hanging test - websocketOverTor doesn't have abortController await sleep(5000) }) diff --git a/packages/backend/src/nest/ipfs/ipfs.service.ts b/packages/backend/src/nest/ipfs/ipfs.service.ts index bd5c4024d9..e27ecd2f92 100644 --- a/packages/backend/src/nest/ipfs/ipfs.service.ts +++ b/packages/backend/src/nest/ipfs/ipfs.service.ts @@ -1,8 +1,9 @@ import { Inject, Injectable } from '@nestjs/common' import { LazyModuleLoader } from '@nestjs/core' -import { create, IPFS } from 'ipfs-core' +import { create, IPFS, Options as IPFSOptions } from 'ipfs-core' import { IPFS_REPO_PATCH } from '../const' import Logger from '../common/logger' +import { Libp2p } from 'libp2p' @Injectable() export class IpfsService { @@ -29,9 +30,12 @@ export class IpfsService { this.logger.error('no libp2p instance') throw new Error('no libp2p instance') } - ipfs = await create({ + const getLibp2p = async (...args: any[]): Promise => { + return libp2pInstance + } + const ipfsConfig: IPFSOptions = { start: false, - libp2p: async () => libp2pInstance, + libp2p: getLibp2p, preload: { enabled: false }, repo: this.ipfsRepoPath, EXPERIMENTAL: { @@ -40,7 +44,9 @@ export class IpfsService { init: { privateKey: peerId, }, - }) + } + this.logger('Creating new ipfs instance') + ipfs = await create(ipfsConfig) this.ipfsInstance = ipfs } catch (error) { this.logger.error('ipfs creation failed', error) diff --git a/packages/backend/src/nest/libp2p/libp2p.service.ts b/packages/backend/src/nest/libp2p/libp2p.service.ts index 5b552217e6..f2264092be 100644 --- a/packages/backend/src/nest/libp2p/libp2p.service.ts +++ b/packages/backend/src/nest/libp2p/libp2p.service.ts @@ -10,7 +10,7 @@ import crypto from 'crypto' import { EventEmitter } from 'events' import { Agent } from 'https' import { createServer } from 'it-ws' -import { Libp2p, createLibp2p } from 'libp2p' +import { Libp2p, createLibp2p, Libp2pOptions } from 'libp2p' import { preSharedKey } from 'libp2p/pnet' import { DateTime } from 'luxon' import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string' @@ -163,7 +163,9 @@ export class Libp2pService extends EventEmitter { } public async createInstance(params: Libp2pNodeParams, startDialImmediately: boolean = false): Promise { + this.logger(`Creating new libp2p instance`) if (this.libp2pInstance) { + this.logger(`Libp2p instance already exists`) return this.libp2pInstance } @@ -171,13 +173,16 @@ export class Libp2pService extends EventEmitter { const maxParallelDials = 2 try { - libp2p = await createLibp2p({ + const libp2pConfig: Libp2pOptions = { start: false, connectionManager: { minConnections: 5, // TODO: increase? maxConnections: 20, // TODO: increase? - dialTimeout: 120000, + dialTimeout: 90_000, maxParallelDials, + maxIncomingPendingConnections: 30, + inboundConnectionThreshold: 30, + inboundUpgradeTimeout: 45_000, autoDial: true, // It's a default but let's set it to have explicit information }, peerId: params.peerId, @@ -196,6 +201,16 @@ export class Libp2pService extends EventEmitter { active: false, }, }, + // ping: { + // maxInboundStreams: 10, + // maxOutboundStreams: 10, + // timeout: 15_000 + // }, + // fetch: { + // maxInboundStreams: 10, + // maxOutboundStreams: 10, + // timeout: 15_000 + // }, transports: [ webSockets({ filter: all, @@ -212,7 +227,8 @@ export class Libp2pService extends EventEmitter { allowPublishToZeroPeers: true, doPX: true, }), - }) + } + libp2p = await createLibp2p(libp2pConfig) } catch (err) { this.logger.error('Create libp2p:', err) throw err diff --git a/packages/backend/src/nest/tor/tor.service.ts b/packages/backend/src/nest/tor/tor.service.ts index 8bbbbb3208..277f6b3288 100644 --- a/packages/backend/src/nest/tor/tor.service.ts +++ b/packages/backend/src/nest/tor/tor.service.ts @@ -56,13 +56,22 @@ export class Tor extends EventEmitter implements OnModuleInit { this.controlPort = port } - mergeDefaultTorParams = (params: TorParams = {}): TorParams => { - const defaultParams = { + private mergeDefaultTorParams(params: TorParams = {}): TorParams { + const defaultParams: TorParams = { '--NumEntryGuards': '3', // See task #1295 + '--LearnCircuitBuildTimeout': '1', + '--CircuitBuildTimeout': '10', + '--KeepalivePeriod': '15', + '--NewCircuitPeriod': '300', } return { ...defaultParams, ...params } } + private mergeDefaultTorParamsAndFlatten(params: TorParams = {}): string[] { + const mergedParams = this.mergeDefaultTorParams(params) + return Array.from(Object.entries(mergedParams)).flat() + } + get torProcessParams(): string[] { return Array.from(Object.entries(this.extraTorProcessParams)).flat() } @@ -134,11 +143,12 @@ export class Tor extends EventEmitter implements OnModuleInit { this.logger(`Sending ${SocketActionTypes.INITIAL_DIAL}`) this.emit(SocketActionTypes.INITIAL_DIAL) clearInterval(this.interval) - resolve() + clearTimeout(this.initTimeout) } }, 2500) this.logger(`Spawned tor with pid(s): ${this.getTorProcessIds()}`) + resolve() } catch (e) { this.logger('Killing tor due to error', e) this.clearHangingTorProcess() @@ -267,7 +277,9 @@ export class Tor extends EventEmitter implements OnModuleInit { `"${this.torDataDirectory}"`, '--HashedControlPassword', this.torPasswordProvider.torHashedPassword, - // ...this.torProcessParams + '--LongLivedPorts', + `80,${this.configOptions.httpTunnelPort},${this.socksPort}`, + ...this.mergeDefaultTorParamsAndFlatten(), ], options ) diff --git a/packages/mobile/ios/TorHandler.swift b/packages/mobile/ios/TorHandler.swift index 1a62ade0f4..788a78261c 100644 --- a/packages/mobile/ios/TorHandler.swift +++ b/packages/mobile/ios/TorHandler.swift @@ -20,6 +20,12 @@ class TorHandler: NSObject { "--ControlPort", "127.0.0.1:\(controlPort)", "--HTTPTunnelPort", "127.0.0.1:\(httpTunnelPort)", "--Log", log_loc, + "--NumEntryGuards", "3", + "--LearnCircuitBuildTimeout", "1", + "--CircuitBuildTimeout", "10", + "--KeepalivePeriod", "15", + "--NewCircuitPeriod", "300", + "--LongLivedPorts", "80,\(socksPort),\(httpTunnelPort)", ] if let dataDir = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)