From 8160eee9ffb0d16f6006985f88f7a36620844a37 Mon Sep 17 00:00:00 2001 From: Peter Date: Wed, 24 Dec 2025 00:27:03 +0300 Subject: [PATCH 01/20] chore(sentry): add some debug logs (#500) * add logs to sentry worker * tune logs * Update index.ts * fix tests --- jest.setup.js | 2 +- lib/worker.ts | 4 +- workers/sentry/src/index.ts | 92 ++++++++++++++++++++++++++++++++----- 3 files changed, 84 insertions(+), 14 deletions(-) diff --git a/jest.setup.js b/jest.setup.js index ba2fc2398..280382c3c 100644 --- a/jest.setup.js +++ b/jest.setup.js @@ -10,7 +10,7 @@ const mockedAmqpChannel = { close: jest.fn(), assertQueue: jest.fn(), prefetch: jest.fn(), - sendToQueue: jest.fn(), + sendToQueue: jest.fn().mockReturnValue(true), on: jest.fn(), consume: jest.fn().mockReturnValue('mockedTag'), }; diff --git a/lib/worker.ts b/lib/worker.ts index f606ef03e..5c535b8aa 100644 --- a/lib/worker.ts +++ b/lib/worker.ts @@ -330,7 +330,9 @@ export abstract class Worker { console.log('handle error'); console.log(e); - HawkCatcher.send(e, context); + HawkCatcher.send(e, Object.assign(context, { + worker: this.type, + })); switch (e.constructor) { case CriticalError: diff --git a/workers/sentry/src/index.ts b/workers/sentry/src/index.ts index 42c5bdfa5..b05940944 100644 --- a/workers/sentry/src/index.ts +++ b/workers/sentry/src/index.ts @@ -39,9 +39,24 @@ export default class SentryEventWorker extends Worker { const [headers, items] = envelope; + if (items.length === 0) { + this.logger.warn('Received envelope with no items'); + return; + } + + let processedCount = 0; + let skippedCount = 0; + for (const item of items) { - await this.handleEnvelopeItem(headers, item, event.projectId); + const result = await this.handleEnvelopeItem(headers, item, event.projectId); + if (result === 'processed') { + processedCount++; + } else if (result === 'skipped') { + skippedCount++; + } } + + this.logger.verbose(`Processed ${processedCount} events, skipped ${skippedCount} non-event items from envelope`); } catch (error) { this.logger.error(`Error handling Sentry event task:`, error); this.logger.info('👇 Here is the problematic event:'); @@ -99,8 +114,9 @@ export default class SentryEventWorker extends Worker { * @param envelopeHeaders - The whole envelope headers * @param item - Sentry item * @param projectId - Sentry project ID + * @returns 'processed' if event was sent, 'skipped' if non-event item, throws error on failure */ - private async handleEnvelopeItem(envelopeHeaders: Envelope[0], item: EnvelopeItem, projectId: string): Promise { + private async handleEnvelopeItem(envelopeHeaders: Envelope[0], item: EnvelopeItem, projectId: string): Promise<'processed' | 'skipped'> { try { const [itemHeader, itemPayload] = item; @@ -112,7 +128,8 @@ export default class SentryEventWorker extends Worker { * Skip non-event items */ if (itemHeader.type !== 'event') { - return; + this.logger.info(`Skipping non-event item of type: ${itemHeader.type}`); + return 'skipped'; } const payloadHasSDK = typeof itemPayload === 'object' && 'sdk' in itemPayload; @@ -121,18 +138,53 @@ export default class SentryEventWorker extends Worker { */ const sentryJsSDK = ['browser', 'react', 'vue', 'angular', 'capacirtor', 'electron']; - const isJsSDK = payloadHasSDK && sentryJsSDK.includes(itemPayload.sdk.name); + /** + * Safely check if SDK name exists and is in the list + * SDK name can be either a simple name like "react" or a full name like "sentry.javascript.react" + */ + const sdkName = payloadHasSDK && itemPayload.sdk && typeof itemPayload.sdk === 'object' && 'name' in itemPayload.sdk + ? itemPayload.sdk.name + : undefined; + + /** + * Check if SDK is a JavaScript-related SDK + * Supports both simple names (e.g., "react") and full names (e.g., "sentry.javascript.react") + */ + const isJsSDK = sdkName !== undefined && typeof sdkName === 'string' && ( + /** + * Exact match for simple SDK names (e.g., "react", "browser") + */ + sentryJsSDK.includes(sdkName) || + /** + * Check if SDK name contains one of the JS SDK names + * Examples: + * - "sentry.javascript.react" matches "react" + * - "sentry.javascript.browser" matches "browser" + * - "@sentry/react" matches "react" + */ + sentryJsSDK.some((jsSDK) => sdkName.includes(jsSDK)) + ); const hawkEvent = this.transformToHawkFormat(envelopeHeaders as EventEnvelope[0], item as EventItem, projectId, isJsSDK); /** - * If we have release attached to the event + * Send task to appropriate worker and check if it was successfully queued */ - if (isJsSDK) { - await this.addTask(WorkerNames.JAVASCRIPT, hawkEvent as JavaScriptEventWorkerTask); - } else { - await this.addTask(WorkerNames.DEFAULT, hawkEvent as DefaultEventWorkerTask); + const workerName = isJsSDK ? WorkerNames.JAVASCRIPT : WorkerNames.DEFAULT; + const taskSent = await this.addTask(workerName, hawkEvent as JavaScriptEventWorkerTask | DefaultEventWorkerTask); + + if (!taskSent) { + /** + * If addTask returns false, the message was not queued (queue full or channel closed) + */ + const error = new Error(`Failed to queue event to ${workerName} worker. Queue may be full or channel closed.`); + this.logger.error(error.message); + this.logger.info('👇 Here is the event that failed to queue:'); + this.logger.json(hawkEvent); + throw error; } + + return 'processed'; } catch (error) { this.logger.error('Error handling envelope item:', error); this.logger.info('👇 Here is the problematic item:'); @@ -162,7 +214,14 @@ export default class SentryEventWorker extends Worker { * convert sent_at from ISO 8601 to Unix timestamp */ const msInSecond = 1000; - const sentAtUnix = Math.floor(new Date(sent_at).getTime() / msInSecond); + const sentAtDate = new Date(sent_at); + const sentAtTime = sentAtDate.getTime(); + + if (isNaN(sentAtTime)) { + throw new Error(`Invalid sent_at timestamp: ${sent_at}`); + } + + const sentAtUnix = Math.floor(sentAtTime / msInSecond); /* eslint-enable @typescript-eslint/naming-convention */ // eslint-disable-next-line @typescript-eslint/no-unused-vars, no-unused-vars @@ -175,9 +234,18 @@ export default class SentryEventWorker extends Worker { * We need to decode it to JSON */ if (eventPayload instanceof Uint8Array) { - const textDecoder = new TextDecoder(); + try { + const textDecoder = new TextDecoder(); + const decoded = textDecoder.decode(eventPayload as Uint8Array); - eventPayload = JSON.parse(textDecoder.decode(eventPayload as Uint8Array)); + try { + eventPayload = JSON.parse(decoded); + } catch (parseError) { + throw new Error(`Failed to parse event payload JSON: ${parseError instanceof Error ? parseError.message : String(parseError)}`); + } + } catch (decodeError) { + throw new Error(`Failed to decode Uint8Array event payload: ${decodeError instanceof Error ? decodeError.message : String(decodeError)}`); + } } const title = composeTitle(eventPayload); From 8844840c0ac48a2f14c5de648fd41aa62ead90b0 Mon Sep 17 00:00:00 2001 From: Peter Date: Wed, 24 Dec 2025 15:55:17 +0300 Subject: [PATCH 02/20] chore(event-email): event email notification now contains link to a particular repetition (#499) * Add repetitionId to event notification flow Introduces the repetitionId field to event notification data structures and templates, allowing emails and notifications to reference specific event repetitions. Updates TypeScript interfaces, worker logic, and email templates to support and display repetitionId where applicable. * fix grouper test --- workers/email/src/templates/emails/event/html.twig | 7 ++++++- workers/email/src/templates/emails/event/text.twig | 7 ++++++- workers/grouper/src/index.ts | 1 + workers/grouper/tests/index.test.ts | 1 + workers/notifier/src/index.ts | 1 + workers/notifier/types/channel.ts | 6 ++++++ workers/notifier/types/notifier-task.ts | 6 ++++++ workers/sender/src/index.ts | 3 ++- workers/sender/types/template-variables/event.ts | 5 +++++ 9 files changed, 34 insertions(+), 3 deletions(-) diff --git a/workers/email/src/templates/emails/event/html.twig b/workers/email/src/templates/emails/event/html.twig index 35be79c6c..535afe3a7 100644 --- a/workers/email/src/templates/emails/event/html.twig +++ b/workers/email/src/templates/emails/event/html.twig @@ -8,6 +8,7 @@ {% set utmParams = 'utm_source=email&utm_medium=transactional&utm_campaign=event' %} {% set event = events[0].event %} + {% set repetitionId = events[0].repetitionId %} {% set daysRepeated = events[0].daysRepeated %} {% set newCount = events[0].newCount %} {% set usersAffected = events[0].usersAffected %} @@ -56,7 +57,11 @@ - {% set eventURL = host ~ '/project/' ~ project._id ~ '/event/' ~ event._id ~ '?' ~ utmParams %} + {% if repetitionId %} + {% set eventURL = host ~ '/project/' ~ project._id ~ '/event/' ~ event._id ~ '/' ~ repetitionId ~ '/overview?' ~ utmParams %} + {% else %} + {% set eventURL = host ~ '/project/' ~ project._id ~ '/event/' ~ event._id ~ '?' ~ utmParams %} + {% endif %} {% include '../../components/button.twig' with {href: eventURL, label: 'Смотреть детали'} %} diff --git a/workers/email/src/templates/emails/event/text.twig b/workers/email/src/templates/emails/event/text.twig index d12c7f00d..f078d7d10 100644 --- a/workers/email/src/templates/emails/event/text.twig +++ b/workers/email/src/templates/emails/event/text.twig @@ -1,4 +1,5 @@ {% set event = events[0].event %} +{% set repetitionId = events[0].repetitionId %} {% set daysRepeated = events[0].daysRepeated %} {% set newCount = events[0].newCount %} {% set usersAffected = events[0].usersAffected %} @@ -25,7 +26,11 @@ Это событие произошло {{ event.totalCount }} {{ pluralize_ru(event.totalCount, ['раз', 'раза', 'раз']) }} за {{ daysRepeated }} {{ pluralize_ru(daysRepeated, ['день', 'дня', 'дней']) }}. -Смотреть детали: {{ host }}/project/{{ project._id }}/event/{{ event._id }}?{{ utmParams }} +{% if repetitionId %} +Смотреть детали: {{ host }}/project/{{ project._id }}/event/{{ event._id }}/{{ repetitionId }}/overview?{{ utmParams }} +{% else %} +Смотреть детали: {{ host }}/project/{{ project._id }}/event/{{ event._id }}/overview?{{ utmParams }} +{% endif %} *** diff --git a/workers/grouper/src/index.ts b/workers/grouper/src/index.ts index 8eef04fce..8d20daf56 100644 --- a/workers/grouper/src/index.ts +++ b/workers/grouper/src/index.ts @@ -265,6 +265,7 @@ export default class GrouperWorker extends Worker { title: task.payload.title, groupHash: uniqueEventHash, isNew: isFirstOccurrence, + repetitionId: repetitionId ? repetitionId.toString() : null, }, }); } diff --git a/workers/grouper/tests/index.test.ts b/workers/grouper/tests/index.test.ts index 153a7952b..ee781e98a 100644 --- a/workers/grouper/tests/index.test.ts +++ b/workers/grouper/tests/index.test.ts @@ -734,6 +734,7 @@ describe('GrouperWorker', () => { title: task.payload.title, groupHash: expect.any(String), isNew: true, + repetitionId: null, }, }); diff --git a/workers/notifier/src/index.ts b/workers/notifier/src/index.ts index a05101c32..d2d17966e 100644 --- a/workers/notifier/src/index.ts +++ b/workers/notifier/src/index.ts @@ -160,6 +160,7 @@ export default class NotifierWorker extends Worker { await this.sendToSenderWorker(channelKey, [ { key: event.groupHash, count: 1, + repetitionId: event.repetitionId, } ]); } } diff --git a/workers/notifier/types/channel.ts b/workers/notifier/types/channel.ts index 3a195c3d7..001a1f273 100644 --- a/workers/notifier/types/channel.ts +++ b/workers/notifier/types/channel.ts @@ -35,6 +35,12 @@ export interface SenderData { * Number of events received */ count: number; + + /** + * ID of the repetition that triggered this notification + * null for first occurrence, ObjectId string for repetitions + */ + repetitionId: string | null; } /** diff --git a/workers/notifier/types/notifier-task.ts b/workers/notifier/types/notifier-task.ts index f773660c9..04bf3abb6 100644 --- a/workers/notifier/types/notifier-task.ts +++ b/workers/notifier/types/notifier-task.ts @@ -14,6 +14,12 @@ export type NotifierEvent = Pick, 'title'> & { * Flag to show if event is received first time */ isNew: boolean; + + /** + * ID of the repetition that triggered this notification + * null for first occurrence, string for repetitions + */ + repetitionId: string | null; }; /** diff --git a/workers/sender/src/index.ts b/workers/sender/src/index.ts index 7f24c1dc3..23fdeb63c 100644 --- a/workers/sender/src/index.ts +++ b/workers/sender/src/index.ts @@ -171,13 +171,14 @@ export default abstract class SenderWorker extends Worker { const eventsData = await Promise.all( events.map( - async ({ key: groupHash, count }: { key: string; count: number }): Promise => { + async ({ key: groupHash, count, repetitionId }: { key: string; count: number; repetitionId?: string | null }): Promise => { const [event, daysRepeated] = await this.getEventDataByGroupHash(projectId, groupHash); return { event, newCount: count, daysRepeated, + repetitionId: repetitionId ?? null, }; } ) diff --git a/workers/sender/types/template-variables/event.ts b/workers/sender/types/template-variables/event.ts index 701444ece..69e7fe3d3 100644 --- a/workers/sender/types/template-variables/event.ts +++ b/workers/sender/types/template-variables/event.ts @@ -25,6 +25,11 @@ export interface TemplateEventData { * Number of affected users for this event */ usersAffected?: number; + + /** + * ID of the particular repetition of occurred event + */ + repetitionId?: string | null; } /** From 21cb047c61f4790175daf44599d6925779e1d94d Mon Sep 17 00:00:00 2001 From: Peter Date: Wed, 24 Dec 2025 18:07:25 +0300 Subject: [PATCH 03/20] fix(sentry): replay skipping improved (#503) * fix sentry replay skipping * lint code * Update index.test.ts --- lib/memoize/index.ts | 2 +- workers/archiver/src/index.ts | 14 ++-- workers/email/scripts/emailOverview.ts | 2 +- workers/javascript/tests/index.test.ts | 1 - workers/limiter/tests/dbHelper.test.ts | 2 +- workers/paymaster/src/index.ts | 5 +- workers/paymaster/tests/index.test.ts | 1 + workers/release/src/index.ts | 1 + workers/sentry/src/index.ts | 39 ++++++++-- workers/sentry/tests/index.test.ts | 100 ++++++++++++++++++++++++- 10 files changed, 146 insertions(+), 21 deletions(-) diff --git a/lib/memoize/index.ts b/lib/memoize/index.ts index a363360cf..c4c62f729 100644 --- a/lib/memoize/index.ts +++ b/lib/memoize/index.ts @@ -45,7 +45,7 @@ export function memoize(options: MemoizeOptions = {}): MethodDecorator { max = 50, ttl = 1000 * 60 * 30, strategy = 'concat', - skipCache = [] + skipCache = [], } = options; /* eslint-enable */ diff --git a/workers/archiver/src/index.ts b/workers/archiver/src/index.ts index 18d39de4d..457f36c55 100644 --- a/workers/archiver/src/index.ts +++ b/workers/archiver/src/index.ts @@ -86,7 +86,7 @@ export default class ArchiverWorker extends Worker { const projects = await this.projectCollection.find({}).project({ _id: 1, - name: 1 + name: 1, }); const projectsData: ReportDataByProject[] = []; @@ -155,11 +155,11 @@ export default class ArchiverWorker extends Worker { await this.projectCollection.updateOne({ _id: project._id, }, - { - $inc: { - archivedEventsCount: deletedCount, - }, - }); + { + $inc: { + archivedEventsCount: deletedCount, + }, + }); } /** @@ -351,7 +351,7 @@ export default class ArchiverWorker extends Worker { this.logger.info('Report notification response:', { status: response?.status, statusText: response?.statusText, - data: response?.data + data: response?.data, }); } diff --git a/workers/email/scripts/emailOverview.ts b/workers/email/scripts/emailOverview.ts index ff36ed82a..a108b86b8 100644 --- a/workers/email/scripts/emailOverview.ts +++ b/workers/email/scripts/emailOverview.ts @@ -156,7 +156,7 @@ class EmailTestServer { tariffPlanId: '5f47f031ff71510040f433c1', password: '1as2eadd321a3cDf', plan: { - name: 'Корпоративный' + name: 'Корпоративный', }, workspaceName: workspace.name, }; diff --git a/workers/javascript/tests/index.test.ts b/workers/javascript/tests/index.test.ts index 6b57d1e4a..531826e89 100644 --- a/workers/javascript/tests/index.test.ts +++ b/workers/javascript/tests/index.test.ts @@ -442,5 +442,4 @@ describe('JavaScript event worker', () => { await worker.finish(); }); - }); diff --git a/workers/limiter/tests/dbHelper.test.ts b/workers/limiter/tests/dbHelper.test.ts index 9b705878f..cded599b5 100644 --- a/workers/limiter/tests/dbHelper.test.ts +++ b/workers/limiter/tests/dbHelper.test.ts @@ -304,7 +304,7 @@ describe('DbHelper', () => { /** * Act */ - await dbHelper.updateWorkspacesEventsCountAndIsBlocked([updatedWorkspace]); + await dbHelper.updateWorkspacesEventsCountAndIsBlocked([ updatedWorkspace ]); /** * Assert diff --git a/workers/paymaster/src/index.ts b/workers/paymaster/src/index.ts index 333a39118..94be50d96 100644 --- a/workers/paymaster/src/index.ts +++ b/workers/paymaster/src/index.ts @@ -160,6 +160,8 @@ export default class PaymasterWorker extends Worker { /** * Finds plan by id from cached plans + * + * @param planId */ private findPlanById(planId: WorkspaceDBScheme['tariffPlanId']): PlanDBScheme | undefined { return this.plans.find((plan) => plan._id.toString() === planId.toString()); @@ -167,6 +169,8 @@ export default class PaymasterWorker extends Worker { /** * Returns workspace plan, refreshes cache when plan is missing + * + * @param workspace */ private async getWorkspacePlan(workspace: WorkspaceDBScheme): Promise { let currentPlan = this.findPlanById(workspace.tariffPlanId); @@ -413,7 +417,6 @@ export default class PaymasterWorker extends Worker { }); } - /** * Sends reminder emails to blocked workspace admins * diff --git a/workers/paymaster/tests/index.test.ts b/workers/paymaster/tests/index.test.ts index 8ad43b4de..51ff31fdd 100644 --- a/workers/paymaster/tests/index.test.ts +++ b/workers/paymaster/tests/index.test.ts @@ -317,6 +317,7 @@ describe('PaymasterWorker', () => { } MockDate.reset(); + return addTaskSpy; }; diff --git a/workers/release/src/index.ts b/workers/release/src/index.ts index 93c2618f6..ea9282dca 100644 --- a/workers/release/src/index.ts +++ b/workers/release/src/index.ts @@ -281,6 +281,7 @@ export default class ReleaseWorker extends Worker { /** * Some bundlers could skip file in the source map content since it duplicates in map name * Like map name bundle.js.map is a source map for a bundle.js + * * @see https://sourcemaps.info/spec.html - format */ originFileName: mapContent.file ?? file.name.replace(/\.map$/, ''), diff --git a/workers/sentry/src/index.ts b/workers/sentry/src/index.ts index b05940944..ff71217b9 100644 --- a/workers/sentry/src/index.ts +++ b/workers/sentry/src/index.ts @@ -67,10 +67,12 @@ export default class SentryEventWorker extends Worker { /** * Filter out binary items that crash parseEnvelope + * Also filters out all Sentry Replay events (replay_event and replay_recording) */ private filterOutBinaryItems(rawEvent: string): string { const lines = rawEvent.split('\n'); const filteredLines = []; + let isInReplayBlock = false; for (let i = 0; i < lines.length; i++) { const line = lines[i]; @@ -90,17 +92,42 @@ export default class SentryEventWorker extends Worker { // Try to parse as JSON to check if it's a header const parsed = JSON.parse(line); - // If it's a replay header, skip this line and the next one (payload) + // Check if this is a replay event type if (parsed.type === 'replay_recording' || parsed.type === 'replay_event') { - // Skip the next line too (which would be the payload) - i++; + // Mark that we're in a replay block and skip this line + isInReplayBlock = true; continue; } - // Keep valid headers and other JSON data - filteredLines.push(line); + // If we're in a replay block, check if this is still part of it + if (isInReplayBlock) { + // Check if this line is part of replay data (segment_id, length, etc.) + if ('segment_id' in parsed || ('length' in parsed && parsed.type !== 'event') || 'replay_id' in parsed) { + // Still in replay block, skip this line + continue; + } + + // If it's a new envelope item (like event), we've exited the replay block + if (parsed.type === 'event' || parsed.type === 'transaction' || parsed.type === 'session') { + isInReplayBlock = false; + } else { + // Unknown type, assume we're still in replay block + continue; + } + } + + // Keep valid headers and other JSON data (not in replay block) + if (!isInReplayBlock) { + filteredLines.push(line); + } } catch { - // If line doesn't parse as JSON, it might be binary data - skip it + // If line doesn't parse as JSON, it might be binary data + // If we're in a replay block, skip it (it's part of replay recording) + if (isInReplayBlock) { + continue; + } + + // If not in replay block and not JSON, it might be corrupted data - skip it continue; } } diff --git a/workers/sentry/tests/index.test.ts b/workers/sentry/tests/index.test.ts index e41c9fc58..acf44e965 100644 --- a/workers/sentry/tests/index.test.ts +++ b/workers/sentry/tests/index.test.ts @@ -806,12 +806,18 @@ describe('SentryEventWorker', () => { event_id: '4c40fee730194a989439a86bf75634111', sent_at: '2025-08-29T10:59:29.952Z', /* eslint-enable @typescript-eslint/naming-convention */ - sdk: { name: 'sentry.javascript.react', version: '9.10.1' }, + sdk: { + name: 'sentry.javascript.react', + version: '9.10.1', + }, }), // Event item header JSON.stringify({ type: 'event' }), // Event item payload - JSON.stringify({ message: 'Test event', level: 'error' }), + JSON.stringify({ + message: 'Test event', + level: 'error', + }), // Replay event item header - should be filtered out JSON.stringify({ type: 'replay_event' }), // Replay event item payload - should be filtered out @@ -822,7 +828,10 @@ describe('SentryEventWorker', () => { /* eslint-enable @typescript-eslint/naming-convention */ }), // Replay recording item header - should be filtered out - JSON.stringify({ type: 'replay_recording', length: 343 }), + JSON.stringify({ + type: 'replay_recording', + length: 343, + }), // Replay recording binary payload - should be filtered out 'binary-data-here-that-is-not-json', ]; @@ -841,6 +850,7 @@ describe('SentryEventWorker', () => { expect(mockedAmqpChannel.sendToQueue).toHaveBeenCalledTimes(1); const addedTaskPayload = getAddTaskPayloadFromLastCall(); + expect(addedTaskPayload).toMatchObject({ payload: expect.objectContaining({ addons: { @@ -852,6 +862,90 @@ describe('SentryEventWorker', () => { }), }); }); + + it('should ignore envelope with only replay_event and replay_recording items', async () => { + /** + * Test case based on real-world scenario where envelope contains only replay data + * This should not crash with "Unexpected end of JSON input" error + */ + const envelopeLines = [ + // Envelope header + JSON.stringify({ + /* eslint-disable @typescript-eslint/naming-convention */ + event_id: '62680958b3ab4497886375e06533d86a', + sent_at: '2025-12-24T13:16:34.580Z', + /* eslint-enable @typescript-eslint/naming-convention */ + sdk: { + name: 'sentry.javascript.react', + version: '10.22.0', + }, + }), + // Replay event item header - should be filtered out + JSON.stringify({ type: 'replay_event' }), + // Replay event item payload (large JSON) - should be filtered out + JSON.stringify({ + /* eslint-disable @typescript-eslint/naming-convention */ + type: 'replay_event', + replay_start_timestamp: 1766582182.757, + timestamp: 1766582194.579, + error_ids: [], + trace_ids: [], + urls: ['https://my.huntio.ru/applicants', 'https://my.huntio.ru/applicants/1270067'], + replay_id: '62680958b3ab4497886375e06533d86a', + segment_id: 1, + replay_type: 'session', + request: { + url: 'https://my.huntio.ru/applicants/1270067', + headers: { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', + }, + }, + event_id: '62680958b3ab4497886375e06533d86a', + environment: 'production', + release: '1.0.7', + sdk: { + integrations: ['InboundFilters', 'FunctionToString', 'BrowserApiErrors', 'Breadcrumbs'], + name: 'sentry.javascript.react', + version: '10.22.0', + settings: { infer_ip: 'auto' }, + }, + user: { + id: 487, + email: 'npr@unicorn-resources.pro', + username: 'Прохорова Наталья', + }, + contexts: { react: { version: '19.1.0' } }, + transaction: '/applicants/1270067', + platform: 'javascript', + /* eslint-enable @typescript-eslint/naming-convention */ + }), + // Replay recording item header - should be filtered out + JSON.stringify({ + type: 'replay_recording', + length: 16385, + }), + /* eslint-disable @typescript-eslint/naming-convention */ + // Segment ID - should be filtered out + JSON.stringify({ segment_id: 1 }), + /* eslint-enable @typescript-eslint/naming-convention */ + // Binary data (simulated) - should be filtered out + 'xnFWy@v$xAlJ=&fS~¾˶IJ { From 2cb3e5b14d0df10613482b57f08aa3e5a8acf5b9 Mon Sep 17 00:00:00 2001 From: Peter Date: Fri, 26 Dec 2025 15:14:18 +0300 Subject: [PATCH 04/20] Log Sentry client_report items for debugging (#505) Added handling for 'client_report' items in Sentry envelopes to log their internals for easier debugging of dropped events and SDK/reporting issues. Decodes payloads as needed and logs errors if decoding fails. --- workers/sentry/src/index.ts | 40 +++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/workers/sentry/src/index.ts b/workers/sentry/src/index.ts index ff71217b9..9ed56c4e5 100644 --- a/workers/sentry/src/index.ts +++ b/workers/sentry/src/index.ts @@ -155,6 +155,46 @@ export default class SentryEventWorker extends Worker { * Skip non-event items */ if (itemHeader.type !== 'event') { + if (itemHeader.type === 'client_report') { + /** + * Sentry "client_report" items are useful for debugging dropped events. + * We log internals here to make diagnosing SDK/reporting issues easier. + */ + try { + let decodedPayload: unknown = itemPayload; + + /** + * Sometimes Sentry parses the itemPayload as a Uint8Array. + * Decode it to JSON so it can be logged meaningfully. + */ + if (decodedPayload instanceof Uint8Array) { + const textDecoder = new TextDecoder(); + decodedPayload = textDecoder.decode(decodedPayload as Uint8Array); + } + + if (typeof decodedPayload === 'string') { + try { + decodedPayload = JSON.parse(decodedPayload); + } catch { + /** + * Keep the raw string if it isn't valid JSON. + */ + } + } + + this.logger.info('Received client_report item; logging internals:'); + this.logger.json({ + envelopeHeaders, + itemHeader, + payload: decodedPayload, + }); + } catch (clientReportError) { + this.logger.warn('Failed to decode/log client_report item:', clientReportError); + this.logger.info('👇 Here is the raw client_report item:'); + this.logger.json(item); + } + } + this.logger.info(`Skipping non-event item of type: ${itemHeader.type}`); return 'skipped'; } From eea987791e7024504493333e7ac1e7a86324b849 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Fri, 23 Jan 2026 22:38:39 +0300 Subject: [PATCH 05/20] bug(sentry): Flatten nested objects in backtrace frame arguments using dot notation (#509) * Initial plan * Implement dot notation for nested objects in backtrace frame arguments Co-authored-by: neSpecc <3684889+neSpecc@users.noreply.github.com> * Fix empty array handling to be consistent with empty objects Co-authored-by: neSpecc <3684889+neSpecc@users.noreply.github.com> * Remove unrelated file changes that should not be affected by the solution Co-authored-by: neSpecc <3684889+neSpecc@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: neSpecc <3684889+neSpecc@users.noreply.github.com> --- workers/sentry/src/utils/converter.ts | 51 +++++++- workers/sentry/tests/converter.test.ts | 156 +++++++++++++++++++++++++ workers/sentry/tests/index.test.ts | 2 +- 3 files changed, 206 insertions(+), 3 deletions(-) diff --git a/workers/sentry/src/utils/converter.ts b/workers/sentry/src/utils/converter.ts index b3fac8492..916f158cc 100644 --- a/workers/sentry/src/utils/converter.ts +++ b/workers/sentry/src/utils/converter.ts @@ -1,6 +1,53 @@ import { BacktraceFrame, DefaultAddons, EventContext, EventData, Json, SentryAddons } from '@hawk.so/types'; import { Event as SentryEvent } from '@sentry/core'; +/** + * Flattens a nested object into an array of strings using dot notation + * For example: {foo: 1, bar: {baz: 2}} becomes ["foo=1", "bar.baz=2"] + * + * @param obj - The object to flatten + * @param prefix - The prefix to use for nested keys (used in recursion) + */ +function flattenObject(obj: unknown, prefix = ''): string[] { + const result: string[] = []; + + if (obj === null || obj === undefined) { + return [ prefix ? `${prefix}=${obj}` : String(obj) ]; + } + + if (typeof obj !== 'object') { + return [ prefix ? `${prefix}=${obj}` : String(obj) ]; + } + + if (Array.isArray(obj)) { + if (obj.length === 0) { + return [ prefix ? `${prefix}=[]` : '[]' ]; + } + + obj.forEach((value, index) => { + const key = prefix ? `${prefix}.${index}` : String(index); + + result.push(...flattenObject(value, key)); + }); + + return result; + } + + const entries = Object.entries(obj); + + if (entries.length === 0) { + return [ prefix ? `${prefix}={}` : '{}' ]; + } + + entries.forEach(([key, value]) => { + const newPrefix = prefix ? `${prefix}.${key}` : key; + + result.push(...flattenObject(value, newPrefix)); + }); + + return result; +} + /** * Compose title from Sentry event payload * @@ -79,8 +126,8 @@ export function composeBacktrace(eventPayload: SentryEvent): EventData { - return `${name}=${value}`; + backtraceFrame.arguments = Object.entries(frame.vars).flatMap(([name, value]) => { + return flattenObject(value, name); }); } diff --git a/workers/sentry/tests/converter.test.ts b/workers/sentry/tests/converter.test.ts index d84d8afab..7c630c696 100644 --- a/workers/sentry/tests/converter.test.ts +++ b/workers/sentry/tests/converter.test.ts @@ -93,6 +93,162 @@ describe('converter utils', () => { }); }); + it('should handle nested objects in vars using dot notation', () => { + const event: SentryEvent = { + exception: { + values: [ { + stacktrace: { + frames: [ { + filename: 'test.js', + lineno: 10, + vars: { + params: { + foo: 1, + bar: 2, + second: { + glass: 3, + }, + }, + }, + } ], + }, + } ], + }, + }; + + const backtrace = composeBacktrace(event); + + expect(backtrace?.[0].arguments).toEqual([ + 'params.foo=1', + 'params.bar=2', + 'params.second.glass=3', + ]); + }); + + it('should handle arrays in vars using dot notation with indices', () => { + const event: SentryEvent = { + exception: { + values: [ { + stacktrace: { + frames: [ { + filename: 'test.js', + lineno: 10, + vars: { + items: ['first', 'second', 'third'], + }, + } ], + }, + } ], + }, + }; + + const backtrace = composeBacktrace(event); + + expect(backtrace?.[0].arguments).toEqual([ + 'items.0=first', + 'items.1=second', + 'items.2=third', + ]); + }); + + it('should handle mixed nested objects and arrays in vars', () => { + const event: SentryEvent = { + exception: { + values: [ { + stacktrace: { + frames: [ { + filename: 'test.js', + lineno: 10, + vars: { + config: { + users: [ + { + name: 'Alice', + age: 30, + }, + { + name: 'Bob', + age: 25, + }, + ], + settings: { + enabled: true, + }, + }, + }, + } ], + }, + } ], + }, + }; + + const backtrace = composeBacktrace(event); + + expect(backtrace?.[0].arguments).toEqual([ + 'config.users.0.name=Alice', + 'config.users.0.age=30', + 'config.users.1.name=Bob', + 'config.users.1.age=25', + 'config.settings.enabled=true', + ]); + }); + + it('should handle null and undefined values in vars', () => { + const event: SentryEvent = { + exception: { + values: [ { + stacktrace: { + frames: [ { + filename: 'test.js', + lineno: 10, + vars: { + nullValue: null, + undefinedValue: undefined, + normalValue: 'test', + }, + } ], + }, + } ], + }, + }; + + const backtrace = composeBacktrace(event); + + expect(backtrace?.[0].arguments).toEqual([ + 'nullValue=null', + 'undefinedValue=undefined', + 'normalValue=test', + ]); + }); + + it('should handle empty objects and arrays in vars', () => { + const event: SentryEvent = { + exception: { + values: [ { + stacktrace: { + frames: [ { + filename: 'test.js', + lineno: 10, + vars: { + emptyObject: {}, + emptyArray: [], + normalValue: 'test', + }, + } ], + }, + } ], + }, + }; + + const backtrace = composeBacktrace(event); + + expect(backtrace?.[0].arguments).toEqual([ + 'emptyObject={}', + 'emptyArray=[]', + 'normalValue=test', + ]); + }); + it('should reverse frames', () => { const event: SentryEvent = { exception: { diff --git a/workers/sentry/tests/index.test.ts b/workers/sentry/tests/index.test.ts index acf44e965..7d964c1be 100644 --- a/workers/sentry/tests/index.test.ts +++ b/workers/sentry/tests/index.test.ts @@ -689,7 +689,7 @@ describe('SentryEventWorker', () => { '__package__=None', '__loader__=<_frozen_importlib_external.SourceFileLoader object at 0x102934cb0>', '__spec__=None', - '__annotations__=[object Object]', + '__annotations__={}', "__builtins__=", "__file__='/Users/nostr/dev/codex/hawk.mono/tests/manual/sentry/sentry-prod.py'", '__cached__=None', From f5f3aae0e9ac7bf3e07eb2d3b00f710d81bad04b Mon Sep 17 00:00:00 2001 From: Peter Date: Mon, 26 Jan 2026 21:31:07 +0300 Subject: [PATCH 06/20] chore(setup): migrate from node 16 to 24 (#512) * Upgrade to Node.js 24 and update TypeScript config Update all workflows, Dockerfiles, and .nvmrc to use Node.js 24 for consistency and latest features. Enable 'moduleResolution: node' and 'skipLibCheck' in tsconfig.json to improve TypeScript compatibility and speed up builds. * upd packages * Update jest.setup.redis-mock.js * Update jest.global-teardown.js * fix tests * Update jest.setup.redis-mock.js * disable redis tests * rm skip * Update jest.config.js * rm skips * lint code --- .github/workflows/check-build.yaml | 2 +- .github/workflows/nodejs.yml | 4 +- .github/workflows/tests.yml | 4 +- .nvmrc | 2 +- Dockerfile | 2 +- dev.Dockerfile | 2 +- jest.config.js | 1 + jest.global-teardown.js | 12 +- jest.setup.mongo-repl-set.js | 6 +- jest.setup.redis-mock.js | 2 +- lib/memoize/index.test.ts | 114 +- lib/utils/payday.test.ts | 5 + ...0000-add-timestamp-index-to-repetitions.js | 141 +- ...7000000-create-release-project-id-index.js | 61 +- ...104000000-add-timestamp-index-to-events.js | 142 +- ...0000-add-compound-index-to-daily-events.js | 146 +- package.json | 16 +- stage.Dockerfile | 2 +- tsconfig.json | 3 +- workers/javascript/src/utils.ts | 8 +- workers/limiter/tests/dbHelper.test.ts | 4 +- workers/release/package.json | 2 +- workers/release/tests/create-mock-bundle.ts | 28 +- workers/sender/src/index.ts | 3 + workers/sentry/src/index.ts | 7 + yarn.lock | 3008 ++++++----------- 26 files changed, 1507 insertions(+), 2220 deletions(-) diff --git a/.github/workflows/check-build.yaml b/.github/workflows/check-build.yaml index f4e2c9004..492d98b76 100644 --- a/.github/workflows/check-build.yaml +++ b/.github/workflows/check-build.yaml @@ -15,7 +15,7 @@ jobs: - name: Set up Node.js uses: actions/setup-node@v2 with: - node-version: '16' + node-version: '24' - name: Install dependencies run: yarn install diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index f4940da2c..ad77f3cf9 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -8,10 +8,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - name: Use Node.js 16.x + - name: Use Node.js 24.x uses: actions/setup-node@v1 with: - node-version: 16.x + node-version: 24.x - name: Cache dependencies uses: actions/cache@v3 with: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 90eb2af60..915aea531 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -12,9 +12,9 @@ jobs: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v2 - - name: Use Node.js 16.x + - name: Use Node.js 24.x uses: actions/setup-node@v4 with: - node-version: 16.x + node-version: 24.x - run: yarn install - run: yarn test diff --git a/.nvmrc b/.nvmrc index 2ab3d4be5..54c65116f 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v16.20.2 +v24 diff --git a/Dockerfile b/Dockerfile index 73f4faba5..1a5a3e0da 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:18.20-slim as build-stage +FROM node:24-slim as build-stage RUN apt update RUN apt install git -y diff --git a/dev.Dockerfile b/dev.Dockerfile index a688df914..cf72abd8b 100644 --- a/dev.Dockerfile +++ b/dev.Dockerfile @@ -1,4 +1,4 @@ -FROM node:16.20-slim +FROM node:24-slim WORKDIR /usr/src/app diff --git a/jest.config.js b/jest.config.js index fa2faf9c5..77f85ed07 100644 --- a/jest.config.js +++ b/jest.config.js @@ -30,4 +30,5 @@ module.exports = { setupFilesAfterEnv: ['./jest.setup.redis-mock.js', './jest.setup.mongo-repl-set.js'], globalTeardown: './jest.global-teardown.js', + testTimeout: 15000, }; diff --git a/jest.global-teardown.js b/jest.global-teardown.js index 447dfc0ec..6440c816c 100644 --- a/jest.global-teardown.js +++ b/jest.global-teardown.js @@ -1,9 +1,17 @@ const process = require('process'); +const mongoTeardown = require('@shelf/jest-mongodb/teardown'); + +module.exports = async () => { + /** + * Cleanup MongoDB Memory Server + * + * @shelf/jest-mongodb should handle this automatically, but we try to ensure cleanup + */ + await mongoTeardown(); -module.exports = () => { if (process.env.CI) { setTimeout(() => { process.exit(0); }, 1000); } -}; \ No newline at end of file +}; diff --git a/jest.setup.mongo-repl-set.js b/jest.setup.mongo-repl-set.js index 14b15095d..48f87790c 100644 --- a/jest.setup.mongo-repl-set.js +++ b/jest.setup.mongo-repl-set.js @@ -15,10 +15,10 @@ beforeAll(async () => { let status = await admin.command({ replSetGetStatus: 1 }).catch(() => null); if (status && status.ok) { - console.log('✅ Replica set already initialized'); + // console.log('✅ Replica set already initialized'); } else { await admin.command({ replSetInitiate: {} }); - console.log('✅ Replica set initiated'); + // console.log('✅ Replica set initiated'); } const startTime = Date.now(); @@ -39,7 +39,7 @@ beforeAll(async () => { await new Promise(resolve => setTimeout(resolve, 1000)); } while (Date.now() - startTime < timeout); - console.log('✅ Replica set is stable'); + // console.log('✅ Replica set is stable'); } catch (err) { console.error('❌ Failed to initiate replica set:', err); } diff --git a/jest.setup.redis-mock.js b/jest.setup.redis-mock.js index 291ded59a..60b53036c 100644 --- a/jest.setup.redis-mock.js +++ b/jest.setup.redis-mock.js @@ -11,7 +11,7 @@ beforeAll(async () => { .start(); const port = redisTestContainer.getMappedPort(6379); - const host = redisTestContainer.getContainerIpAddress(); + const host = redisTestContainer.getHost(); /** * Set environment variable for redisHelper to connect to redis container diff --git a/lib/memoize/index.test.ts b/lib/memoize/index.test.ts index 8953e00bc..705bef953 100644 --- a/lib/memoize/index.test.ts +++ b/lib/memoize/index.test.ts @@ -20,9 +20,16 @@ describe('memoize decorator — per-test inline classes', () => { }); it('should memoize return value with concat strategy across several calls', async () => { + /** + * + */ class Sample { public calls = 0; + /** + * @param a + * @param b + */ @memoize({ strategy: 'concat', ttl: 60_000, max: 50 }) public async run(a: number, b: string) { this.calls += 1; @@ -47,9 +54,16 @@ describe('memoize decorator — per-test inline classes', () => { }); it('should memoize return value with set of arguments with concat strategy across several calls', async () => { + /** + * + */ class Sample { public calls = 0; + /** + * @param a + * @param b + */ @memoize({ strategy: 'concat' }) public async run(a: unknown, b: unknown) { this.calls += 1; @@ -84,9 +98,16 @@ describe('memoize decorator — per-test inline classes', () => { }); it('should memoize return value for stringified objects across several calls', async () => { + /** + * + */ class Sample { public calls = 0; + /** + * @param x + * @param y + */ @memoize({ strategy: 'concat' }) public async run(x: unknown, y: unknown) { this.calls += 1; @@ -105,9 +126,15 @@ describe('memoize decorator — per-test inline classes', () => { }); it('should memoize return value for method with non-default arguments (NaN, Infinity, -0, Symbol, Date, RegExp) still cache same-args', async () => { + /** + * + */ class Sample { public calls = 0; + /** + * @param {...any} args + */ @memoize({ strategy: 'concat' }) public async run(...args: unknown[]) { this.calls += 1; @@ -131,9 +158,15 @@ describe('memoize decorator — per-test inline classes', () => { it('should call crypto hash with blake2b512 algo and base64url digest, should memoize return value with hash strategy', async () => { const hashSpy = jest.spyOn(Crypto, 'hash'); + /** + * + */ class Sample { public calls = 0; + /** + * @param {...any} args + */ @memoize({ strategy: 'hash' }) public async run(...args: unknown[]) { this.calls += 1; @@ -151,9 +184,15 @@ describe('memoize decorator — per-test inline classes', () => { }); it('should not memoize return value with hash strategy and different arguments', async () => { + /** + * + */ class Sample { public calls = 0; + /** + * @param {...any} args + */ @memoize({ strategy: 'hash' }) public async run(...args: unknown[]) { this.calls += 1; @@ -171,9 +210,15 @@ describe('memoize decorator — per-test inline classes', () => { }); it('should memoize return value with hash strategy across several calls with same args', async () => { + /** + * + */ class Sample { public calls = 0; + /** + * @param arg + */ @memoize({ strategy: 'hash' }) public async run(arg: unknown) { this.calls += 1; @@ -196,9 +241,15 @@ describe('memoize decorator — per-test inline classes', () => { const { memoize: memoizeWithMockedTimers } = await import('../memoize/index'); + /** + * + */ class Sample { public calls = 0; + /** + * @param x + */ @memoizeWithMockedTimers({ strategy: 'concat', ttl: 1_000 }) public async run(x: string) { this.calls += 1; @@ -221,9 +272,15 @@ describe('memoize decorator — per-test inline classes', () => { }); it('error calls should never be momized', async () => { + /** + * + */ class Sample { public calls = 0; + /** + * @param x + */ @memoize() public async run(x: number) { this.calls += 1; @@ -245,9 +302,15 @@ describe('memoize decorator — per-test inline classes', () => { }); it('should NOT cache results listed in skipCache (primitives)', async () => { + /** + * + */ class Sample { public calls = 0; - + + /** + * @param kind + */ @memoize({ strategy: 'concat', skipCache: [null, undefined, 0, false, ''] }) public async run(kind: 'null' | 'undef' | 'zero' | 'false' | 'empty') { this.calls += 1; @@ -260,55 +323,68 @@ describe('memoize decorator — per-test inline classes', () => { } } } - + const sample = new Sample(); - + // Each repeated call should invoke the original again because result is in skipCache. await sample.run('null'); await sample.run('null'); - + await sample.run('undef'); await sample.run('undef'); - + await sample.run('zero'); await sample.run('zero'); - + await sample.run('false'); await sample.run('false'); - + await sample.run('empty'); await sample.run('empty'); - + // 5 kinds × 2 calls each = 10 calls, none cached expect(sample.calls).toBe(10); }); - + it('should cache results NOT listed in skipCache', async () => { + /** + * + */ class Sample { public calls = 0; - + + /** + * @param x + */ @memoize({ strategy: 'concat', skipCache: [null, undefined] }) public async run(x: number) { this.calls += 1; + // returns a non-skipped primitive return x * 2; } } - + const sample = new Sample(); - + expect(await sample.run(21)).toBe(42); expect(await sample.run(21)).toBe(42); - + expect(sample.calls).toBe(1); }); - + it('should use equality for skipCache with objects: deep equal objects are cached', async () => { const deepEqualObject = { a: 1 }; - + + /** + * + */ class Sample { public calls = 0; - + + /** + * + */ @memoize({ strategy: 'concat', skipCache: [deepEqualObject] }) public async run() { this.calls += 1; @@ -316,12 +392,12 @@ describe('memoize decorator — per-test inline classes', () => { return { a: 1 }; } } - + const sample = new Sample(); - + const first = await sample.run(); const second = await sample.run(); - + expect(first).toEqual({ a: 1 }); expect(second).toBe(first); expect(sample.calls).toBe(1); diff --git a/lib/utils/payday.test.ts b/lib/utils/payday.test.ts index beaecd0eb..0041ef13e 100644 --- a/lib/utils/payday.test.ts +++ b/lib/utils/payday.test.ts @@ -17,9 +17,11 @@ const resetMockedNow = (): void => { // Override Date constructor const RealDate = Date; + global.Date = class extends RealDate { /** * Constructor for mocked Date class + * * @param args - arguments passed to Date constructor */ constructor(...args: unknown[]) { @@ -30,6 +32,9 @@ global.Date = class extends RealDate { } } + /** + * + */ public static now(): number { return mockedNow !== null ? mockedNow : RealDate.now(); } diff --git a/migrations/20250911000000-add-timestamp-index-to-repetitions.js b/migrations/20250911000000-add-timestamp-index-to-repetitions.js index 02509475c..5c7b3ba60 100644 --- a/migrations/20250911000000-add-timestamp-index-to-repetitions.js +++ b/migrations/20250911000000-add-timestamp-index-to-repetitions.js @@ -1,84 +1,83 @@ const timestampIndexName = 'timestamp'; module.exports = { - async up(db) { - const collections = await db.listCollections({}, { - authorizedCollections: true, - nameOnly: true, - }).toArray(); - - const targetCollections = []; - - collections.forEach((collection) => { - if (/repetitions/.test(collection.name)) { - targetCollections.push(collection.name); - } - }); + async up(db) { + const collections = await db.listCollections({}, { + authorizedCollections: true, + nameOnly: true, + }).toArray(); - console.log(`${targetCollections.length} collections will be updated.`); + const targetCollections = []; - let currentCollectionNumber = 1; + collections.forEach((collection) => { + if (/repetitions/.test(collection.name)) { + targetCollections.push(collection.name); + } + }); - for (const collectionName of targetCollections) { - console.log(`${collectionName} in process.`); - console.log(`${currentCollectionNumber} of ${targetCollections.length} in process.`); - try { - const hasIndexAlready = await db.collection(collectionName).indexExists(timestampIndexName); + console.log(`${targetCollections.length} collections will be updated.`); - if (!hasIndexAlready) { - await db.collection(collectionName).createIndex({ - timestamp: 1, - }, { - name: timestampIndexName, - sparse: true, - background: true, - }); - console.log(`Index ${timestampIndexName} created for ${collectionName}`); - } else { - console.log(`Index ${timestampIndexName} already exists for ${collectionName}`); - } - } catch (error) { - console.error(`Error adding index to ${collectionName}:`, error); - } - currentCollectionNumber++; - } - }, - async down(db) { - const collections = await db.listCollections({}, { - authorizedCollections: true, - nameOnly: true, - }).toArray(); - - const targetCollections = []; + let currentCollectionNumber = 1; - collections.forEach((collection) => { - if (/repetitions/.test(collection.name)) { - targetCollections.push(collection.name); - } - }); - - console.log(`${targetCollections.length} collections will be updated.`); + for (const collectionName of targetCollections) { + console.log(`${collectionName} in process.`); + console.log(`${currentCollectionNumber} of ${targetCollections.length} in process.`); + try { + const hasIndexAlready = await db.collection(collectionName).indexExists(timestampIndexName); - let currentCollectionNumber = 1; + if (!hasIndexAlready) { + await db.collection(collectionName).createIndex({ + timestamp: 1, + }, { + name: timestampIndexName, + sparse: true, + background: true, + }); + console.log(`Index ${timestampIndexName} created for ${collectionName}`); + } else { + console.log(`Index ${timestampIndexName} already exists for ${collectionName}`); + } + } catch (error) { + console.error(`Error adding index to ${collectionName}:`, error); + } + currentCollectionNumber++; + } + }, + async down(db) { + const collections = await db.listCollections({}, { + authorizedCollections: true, + nameOnly: true, + }).toArray(); - for (const collectionName of targetCollections) { - console.log(`${collectionName} in process.`); - console.log(`${currentCollectionNumber} of ${targetCollections.length} in process.`); + const targetCollections = []; - try { - const hasIndexAlready = await db.collection(collectionName).indexExists(timestampIndexName); - if (hasIndexAlready) { - await db.collection(collectionName).dropIndex(timestampIndexName); - console.log(`Index ${timestampIndexName} dropped for ${collectionName}`); - } else { - console.log(`Index ${timestampIndexName} does not exist for ${collectionName}, skipping drop.`); - } - } catch (error) { - console.error(`Error dropping index from ${collectionName}:`, error); - } - currentCollectionNumber++; - } + collections.forEach((collection) => { + if (/repetitions/.test(collection.name)) { + targetCollections.push(collection.name); + } + }); - + console.log(`${targetCollections.length} collections will be updated.`); + + let currentCollectionNumber = 1; + + for (const collectionName of targetCollections) { + console.log(`${collectionName} in process.`); + console.log(`${currentCollectionNumber} of ${targetCollections.length} in process.`); + + try { + const hasIndexAlready = await db.collection(collectionName).indexExists(timestampIndexName); + + if (hasIndexAlready) { + await db.collection(collectionName).dropIndex(timestampIndexName); + console.log(`Index ${timestampIndexName} dropped for ${collectionName}`); + } else { + console.log(`Index ${timestampIndexName} does not exist for ${collectionName}, skipping drop.`); + } + } catch (error) { + console.error(`Error dropping index from ${collectionName}:`, error); + } + currentCollectionNumber++; } -} \ No newline at end of file + }, +}; \ No newline at end of file diff --git a/migrations/20250917000000-create-release-project-id-index.js b/migrations/20250917000000-create-release-project-id-index.js index 9fee1934e..5f0916cf7 100644 --- a/migrations/20250917000000-create-release-project-id-index.js +++ b/migrations/20250917000000-create-release-project-id-index.js @@ -6,12 +6,23 @@ module.exports = { const pairs = await db.collection(collectionName).aggregate([ { $group: { - _id: { projectId: '$projectId', release: '$release' }, + _id: { + projectId: '$projectId', + release: '$release', + }, count: { $sum: 1 }, }, }, - { $project: { _id: 0, projectId: '$_id.projectId', release: '$_id.release', count: 1 } }, - ]).toArray(); + { + $project: { + _id: 0, + projectId: '$_id.projectId', + release: '$_id.release', + count: 1, + }, + }, + ]) + .toArray(); console.log(`Found ${pairs.length} unique (projectId, release) pairs to process.`); @@ -23,7 +34,15 @@ module.exports = { try { const docs = await db.collection(collectionName) - .find({ projectId, release }, { projection: { files: 1, commits: 1 } }) + .find({ + projectId, + release, + }, { + projection: { + files: 1, + commits: 1, + }, + }) .toArray(); const filesByName = new Map(); @@ -56,8 +75,24 @@ module.exports = { * Replace all docs for this pair with a single consolidated doc */ const ops = [ - { deleteMany: { filter: { projectId, release } } }, - { insertOne: { document: { projectId, release, files: mergedFiles, commits: mergedCommits } } }, + { + deleteMany: { + filter: { + projectId, + release, + }, + }, + }, + { + insertOne: { + document: { + projectId, + release, + files: mergedFiles, + commits: mergedCommits, + }, + }, + }, ]; await db.collection(collectionName).bulkWrite(ops, { ordered: true }); @@ -72,10 +107,18 @@ module.exports = { */ try { const hasIndex = await db.collection(collectionName).indexExists(indexName); + if (!hasIndex) { await db.collection(collectionName).createIndex( - { projectId: 1, release: 1 }, - { name: indexName, unique: true, background: true } + { + projectId: 1, + release: 1, + }, + { + name: indexName, + unique: true, + background: true, + } ); console.log(`Index ${indexName} created on ${collectionName} (projectId, release unique).`); } else { @@ -84,13 +127,13 @@ module.exports = { } catch (err) { console.error(`Error creating index ${indexName} on ${collectionName}:`, err); } - }, async down(db) { console.log(`Dropping index ${indexName} from ${collectionName}...`); try { const hasIndex = await db.collection(collectionName).indexExists(indexName); + if (hasIndex) { await db.collection(collectionName).dropIndex(indexName); console.log(`Index ${indexName} dropped from ${collectionName}.`); diff --git a/migrations/20251104000000-add-timestamp-index-to-events.js b/migrations/20251104000000-add-timestamp-index-to-events.js index 83a34d32d..c318b7e11 100644 --- a/migrations/20251104000000-add-timestamp-index-to-events.js +++ b/migrations/20251104000000-add-timestamp-index-to-events.js @@ -8,87 +8,87 @@ const timestampIndexName = 'timestamp'; module.exports = { - async up(db) { - const collections = await db.listCollections({}, { - authorizedCollections: true, - nameOnly: true, - }).toArray(); - - const targetCollections = []; - - collections.forEach((collection) => { - if (/events:/.test(collection.name)) { - targetCollections.push(collection.name); - } - }); - - console.log(`${targetCollections.length} events collections will be updated.`); - - let currentCollectionNumber = 1; - - for (const collectionName of targetCollections) { - console.log(`${collectionName} in process.`); - console.log(`${currentCollectionNumber} of ${targetCollections.length} in process.`); - - try { - const hasIndexAlready = await db.collection(collectionName).indexExists(timestampIndexName); - - if (!hasIndexAlready) { - await db.collection(collectionName).createIndex({ - timestamp: 1, - }, { - name: timestampIndexName, - sparse: true, - background: true, - }); - console.log(`Index ${timestampIndexName} created for ${collectionName}`); - } else { - console.log(`Index ${timestampIndexName} already exists for ${collectionName}`); - } - } catch (error) { - console.error(`Error adding index to ${collectionName}:`, error); - } - - currentCollectionNumber++; + async up(db) { + const collections = await db.listCollections({}, { + authorizedCollections: true, + nameOnly: true, + }).toArray(); + + const targetCollections = []; + + collections.forEach((collection) => { + if (/events:/.test(collection.name)) { + targetCollections.push(collection.name); + } + }); + + console.log(`${targetCollections.length} events collections will be updated.`); + + let currentCollectionNumber = 1; + + for (const collectionName of targetCollections) { + console.log(`${collectionName} in process.`); + console.log(`${currentCollectionNumber} of ${targetCollections.length} in process.`); + + try { + const hasIndexAlready = await db.collection(collectionName).indexExists(timestampIndexName); + + if (!hasIndexAlready) { + await db.collection(collectionName).createIndex({ + timestamp: 1, + }, { + name: timestampIndexName, + sparse: true, + background: true, + }); + console.log(`Index ${timestampIndexName} created for ${collectionName}`); + } else { + console.log(`Index ${timestampIndexName} already exists for ${collectionName}`); } - }, + } catch (error) { + console.error(`Error adding index to ${collectionName}:`, error); + } - async down(db) { - const collections = await db.listCollections({}, { - authorizedCollections: true, - nameOnly: true, - }).toArray(); + currentCollectionNumber++; + } + }, - const targetCollections = []; + async down(db) { + const collections = await db.listCollections({}, { + authorizedCollections: true, + nameOnly: true, + }).toArray(); - collections.forEach((collection) => { - if (/events:/.test(collection.name)) { - targetCollections.push(collection.name); - } - }); + const targetCollections = []; - console.log(`${targetCollections.length} events collections will be updated.`); + collections.forEach((collection) => { + if (/events:/.test(collection.name)) { + targetCollections.push(collection.name); + } + }); - let currentCollectionNumber = 1; + console.log(`${targetCollections.length} events collections will be updated.`); - for (const collectionName of targetCollections) { - console.log(`${collectionName} in process.`); - console.log(`${currentCollectionNumber} of ${targetCollections.length} in process.`); + let currentCollectionNumber = 1; - try { - const hasIndexAlready = await db.collection(collectionName).indexExists(timestampIndexName); + for (const collectionName of targetCollections) { + console.log(`${collectionName} in process.`); + console.log(`${currentCollectionNumber} of ${targetCollections.length} in process.`); - if (hasIndexAlready) { - await db.collection(collectionName).dropIndex(timestampIndexName); - console.log(`Index ${timestampIndexName} dropped for ${collectionName}`); - } else { - console.log(`Index ${timestampIndexName} does not exist for ${collectionName}, skipping drop.`); - } - } catch (error) { - console.error(`Error dropping index from ${collectionName}:`, error); - } + try { + const hasIndexAlready = await db.collection(collectionName).indexExists(timestampIndexName); - currentCollectionNumber++; + if (hasIndexAlready) { + await db.collection(collectionName).dropIndex(timestampIndexName); + console.log(`Index ${timestampIndexName} dropped for ${collectionName}`); + } else { + console.log(`Index ${timestampIndexName} does not exist for ${collectionName}, skipping drop.`); } + } catch (error) { + console.error(`Error dropping index from ${collectionName}:`, error); + } + + currentCollectionNumber++; } + }, }; diff --git a/migrations/20251105000000-add-compound-index-to-daily-events.js b/migrations/20251105000000-add-compound-index-to-daily-events.js index 4ffe380d9..8140371ea 100644 --- a/migrations/20251105000000-add-compound-index-to-daily-events.js +++ b/migrations/20251105000000-add-compound-index-to-daily-events.js @@ -5,90 +5,88 @@ const indexName = 'groupingTimestampAndLastRepetitionTimeAndId'; module.exports = { - async up(db) { - const collections = await db.listCollections({}, { - authorizedCollections: true, - nameOnly: true, - }).toArray(); - - const targetCollections = []; - - collections.forEach((collection) => { - if (/dailyEvents:/.test(collection.name)) { - targetCollections.push(collection.name); - } - }); - - console.log(`${targetCollections.length} dailyEvents collections will be updated.`); - - let currentCollectionNumber = 1; - - for (const collectionName of targetCollections) { - console.log(`${collectionName} in process.`); - console.log(`${currentCollectionNumber} of ${targetCollections.length} in process.`); - - try { - const hasIndexAlready = await db.collection(collectionName).indexExists(indexName); - - if (!hasIndexAlready) { - await db.collection(collectionName).createIndex({ - groupingTimestamp: -1, - lastRepetitionTime: -1, - _id: -1, - }, { - name: indexName, - background: true, - }); - console.log(`Index ${indexName} created for ${collectionName}`); - } else { - console.log(`Index ${indexName} already exists for ${collectionName}`); - } - } catch (error) { - console.error(`Error adding index to ${collectionName}:`, error); - } - - currentCollectionNumber++; + async up(db) { + const collections = await db.listCollections({}, { + authorizedCollections: true, + nameOnly: true, + }).toArray(); + + const targetCollections = []; + + collections.forEach((collection) => { + if (/dailyEvents:/.test(collection.name)) { + targetCollections.push(collection.name); + } + }); + + console.log(`${targetCollections.length} dailyEvents collections will be updated.`); + + let currentCollectionNumber = 1; + + for (const collectionName of targetCollections) { + console.log(`${collectionName} in process.`); + console.log(`${currentCollectionNumber} of ${targetCollections.length} in process.`); + + try { + const hasIndexAlready = await db.collection(collectionName).indexExists(indexName); + + if (!hasIndexAlready) { + await db.collection(collectionName).createIndex({ + groupingTimestamp: -1, + lastRepetitionTime: -1, + _id: -1, + }, { + name: indexName, + background: true, + }); + console.log(`Index ${indexName} created for ${collectionName}`); + } else { + console.log(`Index ${indexName} already exists for ${collectionName}`); } - }, + } catch (error) { + console.error(`Error adding index to ${collectionName}:`, error); + } - async down(db) { - const collections = await db.listCollections({}, { - authorizedCollections: true, - nameOnly: true, - }).toArray(); + currentCollectionNumber++; + } + }, - const targetCollections = []; + async down(db) { + const collections = await db.listCollections({}, { + authorizedCollections: true, + nameOnly: true, + }).toArray(); - collections.forEach((collection) => { - if (/dailyEvents:/.test(collection.name)) { - targetCollections.push(collection.name); - } - }); + const targetCollections = []; - console.log(`${targetCollections.length} dailyEvents collections will be updated.`); + collections.forEach((collection) => { + if (/dailyEvents:/.test(collection.name)) { + targetCollections.push(collection.name); + } + }); - let currentCollectionNumber = 1; + console.log(`${targetCollections.length} dailyEvents collections will be updated.`); - for (const collectionName of targetCollections) { - console.log(`${collectionName} in process.`); - console.log(`${currentCollectionNumber} of ${targetCollections.length} in process.`); + let currentCollectionNumber = 1; - try { - const hasIndexAlready = await db.collection(collectionName).indexExists(indexName); + for (const collectionName of targetCollections) { + console.log(`${collectionName} in process.`); + console.log(`${currentCollectionNumber} of ${targetCollections.length} in process.`); - if (hasIndexAlready) { - await db.collection(collectionName).dropIndex(indexName); - console.log(`Index ${indexName} dropped for ${collectionName}`); - } else { - console.log(`Index ${indexName} does not exist for ${collectionName}, skipping drop.`); - } - } catch (error) { - console.error(`Error dropping index from ${collectionName}:`, error); - } + try { + const hasIndexAlready = await db.collection(collectionName).indexExists(indexName); - currentCollectionNumber++; + if (hasIndexAlready) { + await db.collection(collectionName).dropIndex(indexName); + console.log(`Index ${indexName} dropped for ${collectionName}`); + } else { + console.log(`Index ${indexName} does not exist for ${collectionName}, skipping drop.`); } + } catch (error) { + console.error(`Error dropping index from ${collectionName}:`, error); + } + + currentCollectionNumber++; } + }, }; - - diff --git a/package.json b/package.json index 4cc16ed45..1e8e621a9 100644 --- a/package.json +++ b/package.json @@ -53,11 +53,11 @@ "@babel/parser": "^7.26.9", "@babel/traverse": "7.26.9", "@hawk.so/nodejs": "^3.1.1", - "@hawk.so/types": "^0.2.0", + "@hawk.so/types": "^0.5.7", "@types/amqplib": "^0.8.2", - "@types/jest": "^29.2.3", + "@types/jest": "^29.5.14", "@types/mongodb": "^3.5.15", - "@types/node": "^18.11.9", + "@types/node": "^20.17.0", "amqplib": "^0.8.0", "codex-accounting-sdk": "codex-team/codex-accounting-sdk", "debug": "^4.1.1", @@ -81,15 +81,15 @@ "@shelf/jest-mongodb": "^1.2.3", "eslint": "^7.14.0", "eslint-config-codex": "^1.6.1", - "jest": "^29.2.2", - "jest-mock": "29.2.0", + "jest": "^29.7.0", + "jest-mock": "^29.7.0", "nodemon": "^2.0.3", "random-words": "^1.1.1", "rimraf": "4.4.1", - "testcontainers": "^3.0.0", - "ts-jest": "^29.0.3", + "testcontainers": "^11.11.0", + "ts-jest": "^29.2.0", "wait-for-expect": "^3.0.2", - "webpack": "^4.43.0", + "webpack": "^5.95.0", "yargs": "^15.3.1" } } diff --git a/stage.Dockerfile b/stage.Dockerfile index 0679f6336..7491bff1e 100644 --- a/stage.Dockerfile +++ b/stage.Dockerfile @@ -1,4 +1,4 @@ -FROM node:16.20-slim as build-stage +FROM node:24-slim as build-stage RUN apt update RUN apt install git -y diff --git a/tsconfig.json b/tsconfig.json index 8dbeae476..0238c3f56 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -40,7 +40,8 @@ // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ /* Module Resolution Options */ - // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + "skipLibCheck": true, /* Skip type checking of declaration files from node_modules */ // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ diff --git a/workers/javascript/src/utils.ts b/workers/javascript/src/utils.ts index 6cc8c38b4..feb77a6f6 100644 --- a/workers/javascript/src/utils.ts +++ b/workers/javascript/src/utils.ts @@ -211,7 +211,7 @@ export function getFunctionContext(sourceCode: string, line: number, sourcePath? */ ClassDeclaration(path) { if (path.node.loc && path.node.loc.start.line <= targetLine && path.node.loc.end.line >= targetLine) { - console.log(`class declaration: loc: ${path.node.loc}, targetLine: ${targetLine}, node.start.line: ${path.node.loc.start.line}, node.end.line: ${path.node.loc.end.line}`); + // console.log(`class declaration: loc: ${path.node.loc}, targetLine: ${targetLine}, node.start.line: ${path.node.loc.start.line}, node.end.line: ${path.node.loc.end.line}`); className = path.node.id.name || null; } @@ -224,7 +224,7 @@ export function getFunctionContext(sourceCode: string, line: number, sourcePath? */ ClassMethod(path) { if (path.node.loc && path.node.loc.start.line <= targetLine && path.node.loc.end.line >= targetLine) { - console.log(`class declaration: loc: ${path.node.loc}, targetLine: ${targetLine}, node.start.line: ${path.node.loc.start.line}, node.end.line: ${path.node.loc.end.line}`); + // console.log(`class declaration: loc: ${path.node.loc}, targetLine: ${targetLine}, node.start.line: ${path.node.loc.start.line}, node.end.line: ${path.node.loc.end.line}`); // Handle different key types if (path.node.key.type === 'Identifier') { @@ -240,7 +240,7 @@ export function getFunctionContext(sourceCode: string, line: number, sourcePath? */ FunctionDeclaration(path) { if (path.node.loc && path.node.loc.start.line <= targetLine && path.node.loc.end.line >= targetLine) { - console.log(`function declaration: loc: ${path.node.loc}, targetLine: ${targetLine}, node.start.line: ${path.node.loc.start.line}, node.end.line: ${path.node.loc.end.line}`); + // console.log(`function declaration: loc: ${path.node.loc}, targetLine: ${targetLine}, node.start.line: ${path.node.loc.start.line}, node.end.line: ${path.node.loc.end.line}`); functionName = path.node.id.name || null; isAsync = path.node.async; @@ -259,7 +259,7 @@ export function getFunctionContext(sourceCode: string, line: number, sourcePath? path.node.loc.start.line <= targetLine && path.node.loc.end.line >= targetLine ) { - console.log(`variable declaration: node.type: ${path.node.init.type}, targetLine: ${targetLine}, `); + // console.log(`variable declaration: node.type: ${path.node.init.type}, targetLine: ${targetLine}, `); // Handle different id types if (path.node.id.type === 'Identifier') { diff --git a/workers/limiter/tests/dbHelper.test.ts b/workers/limiter/tests/dbHelper.test.ts index cded599b5..3a4a49f80 100644 --- a/workers/limiter/tests/dbHelper.test.ts +++ b/workers/limiter/tests/dbHelper.test.ts @@ -131,7 +131,7 @@ describe('DbHelper', () => { await planCollection.insertMany(Object.values(mockedPlans)); dbHelper = new DbHelper(projectCollection, workspaceCollection, db); - }); + }, 30000); // 30 seconds timeout for MongoDB connection and setup beforeEach(async () => { await projectCollection.deleteMany({}); @@ -460,4 +460,4 @@ describe('DbHelper', () => { afterAll(async () => { await connection.close(); }); -}); \ No newline at end of file +}); diff --git a/workers/release/package.json b/workers/release/package.json index a108169b7..a886e52b3 100644 --- a/workers/release/package.json +++ b/workers/release/package.json @@ -10,6 +10,6 @@ "private": true, "devDependencies": { "rimraf": "^3.0.0", - "webpack": "^4.39.3" + "webpack": "^5.95.0" } } diff --git a/workers/release/tests/create-mock-bundle.ts b/workers/release/tests/create-mock-bundle.ts index 8dff35fb4..850594f54 100644 --- a/workers/release/tests/create-mock-bundle.ts +++ b/workers/release/tests/create-mock-bundle.ts @@ -27,27 +27,49 @@ export default class MockBundle { entry: this.entry, output: { path: this.outputDir, - libraryExport: 'default', + filename: 'main.js', + library: { + type: 'commonjs2', + export: 'default', + }, }, devtool: 'source-map', + /** + * Webpack 5 requires explicit target configuration + */ + target: 'node', }, (err, stats) => { if (err) { + console.error('[MockBundle] Webpack compilation error:', err); reject(err); return; } + if (!stats) { + reject(new Error('Webpack stats is undefined')); + + return; + } + const info = stats.toJson(); if (stats.hasErrors()) { + console.error('[MockBundle] Webpack compilation errors:'); + // eslint-disable-next-line @typescript-eslint/no-magic-numbers + console.error(JSON.stringify(info.errors, null, 2)); reject(info.errors); + + return; } if (stats.hasWarnings()) { - console.warn(info.warnings); + console.warn('[MockBundle] Webpack compilation warnings:'); + // eslint-disable-next-line @typescript-eslint/no-magic-numbers + console.warn(JSON.stringify(info.warnings, null, 2)); } - resolve(info); + resolve(); }); }); } diff --git a/workers/sender/src/index.ts b/workers/sender/src/index.ts index 23fdeb63c..c4c243b9b 100644 --- a/workers/sender/src/index.ts +++ b/workers/sender/src/index.ts @@ -301,6 +301,9 @@ export default abstract class SenderWorker extends Worker { })); } + /** + * @param task + */ private async handleBlockedWorkspaceReminderTask(task: SenderWorkerBlockedWorkspaceReminderTask): Promise { const eventType = 'blocked-workspace-reminder'; diff --git a/workers/sentry/src/index.ts b/workers/sentry/src/index.ts index 9ed56c4e5..d9e3dde98 100644 --- a/workers/sentry/src/index.ts +++ b/workers/sentry/src/index.ts @@ -41,6 +41,7 @@ export default class SentryEventWorker extends Worker { if (items.length === 0) { this.logger.warn('Received envelope with no items'); + return; } @@ -49,6 +50,7 @@ export default class SentryEventWorker extends Worker { for (const item of items) { const result = await this.handleEnvelopeItem(headers, item, event.projectId); + if (result === 'processed') { processedCount++; } else if (result === 'skipped') { @@ -68,6 +70,8 @@ export default class SentryEventWorker extends Worker { /** * Filter out binary items that crash parseEnvelope * Also filters out all Sentry Replay events (replay_event and replay_recording) + * + * @param rawEvent */ private filterOutBinaryItems(rawEvent: string): string { const lines = rawEvent.split('\n'); @@ -169,6 +173,7 @@ export default class SentryEventWorker extends Worker { */ if (decodedPayload instanceof Uint8Array) { const textDecoder = new TextDecoder(); + decodedPayload = textDecoder.decode(decodedPayload as Uint8Array); } @@ -196,6 +201,7 @@ export default class SentryEventWorker extends Worker { } this.logger.info(`Skipping non-event item of type: ${itemHeader.type}`); + return 'skipped'; } const payloadHasSDK = typeof itemPayload === 'object' && 'sdk' in itemPayload; @@ -245,6 +251,7 @@ export default class SentryEventWorker extends Worker { * If addTask returns false, the message was not queued (queue full or channel closed) */ const error = new Error(`Failed to queue event to ${workerName} worker. Queue may be full or channel closed.`); + this.logger.error(error.message); this.logger.info('👇 Here is the event that failed to queue:'); this.logger.json(hawkEvent); diff --git a/yarn.lock b/yarn.lock index 49e3fd527..3fccdbef8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -358,6 +358,34 @@ minimatch "^3.0.4" strip-json-comments "^3.1.1" +"@grpc/grpc-js@^1.11.1": + version "1.14.3" + resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.14.3.tgz#4c9b817a900ae4020ddc28515ae4b52c78cfb8da" + integrity sha512-Iq8QQQ/7X3Sac15oB6p0FmUg/klxQvXLeileoqrTRGJYLV+/9tubbr9ipz0GKHjmXVsgFPo/+W+2cA8eNcR+XA== + dependencies: + "@grpc/proto-loader" "^0.8.0" + "@js-sdsl/ordered-map" "^4.4.2" + +"@grpc/proto-loader@^0.7.13": + version "0.7.15" + resolved "https://registry.yarnpkg.com/@grpc/proto-loader/-/proto-loader-0.7.15.tgz#4cdfbf35a35461fc843abe8b9e2c0770b5095e60" + integrity sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ== + dependencies: + lodash.camelcase "^4.3.0" + long "^5.0.0" + protobufjs "^7.2.5" + yargs "^17.7.2" + +"@grpc/proto-loader@^0.8.0": + version "0.8.0" + resolved "https://registry.yarnpkg.com/@grpc/proto-loader/-/proto-loader-0.8.0.tgz#b6c324dd909c458a0e4aa9bfd3d69cf78a4b9bd8" + integrity sha512-rc1hOQtjIWGxcxpb9aHAfLpIctjEnsDehj0DAiVfBlmT84uvR0uUtN2hEi/ecvWVjXUGf5qPF4qEgiLOx1YIMQ== + dependencies: + lodash.camelcase "^4.3.0" + long "^5.0.0" + protobufjs "^7.5.3" + yargs "^17.7.2" + "@hawk.so/nodejs@^3.1.1": version "3.1.1" resolved "https://registry.npmjs.org/@hawk.so/nodejs/-/nodejs-3.1.1.tgz" @@ -374,12 +402,12 @@ dependencies: "@types/mongodb" "^3.5.34" -"@hawk.so/types@^0.2.0": - version "0.2.0" - resolved "https://registry.npmjs.org/@hawk.so/types/-/types-0.2.0.tgz" - integrity sha512-KCdkQiqXzD4yrPDskzwj/kcxh84myJudKp5xQh01tisiwrwXMvdF5hf72YYFDy2/HwWTCV4qX4Dxb5RI4De1tw== +"@hawk.so/types@^0.5.7": + version "0.5.7" + resolved "https://registry.yarnpkg.com/@hawk.so/types/-/types-0.5.7.tgz#964af0ad780998820801708292dcb06c390fb9a3" + integrity sha512-ccKVQkFcgTpIe9VVQiz94mCXvGk0Zn6uDyHONlXpS3E0j037eISaw2wUsddzAzKntXYbnFQbxah+zmx35KkVtA== dependencies: - "@types/mongodb" "^3.5.34" + bson "^7.0.0" "@humanwhocodes/config-array@^0.5.0": version "0.5.0" @@ -395,6 +423,18 @@ resolved "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz" integrity sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w== +"@isaacs/cliui@^8.0.2": + version "8.0.2" + resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" + integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== + dependencies: + string-width "^5.1.2" + string-width-cjs "npm:string-width@^4.2.0" + strip-ansi "^7.0.1" + strip-ansi-cjs "npm:strip-ansi@^6.0.1" + wrap-ansi "^8.1.0" + wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" + "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" resolved "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz" @@ -591,7 +631,7 @@ slash "^3.0.0" write-file-atomic "^4.0.2" -"@jest/types@^29.2.0", "@jest/types@^29.6.3": +"@jest/types@^29.6.3": version "29.6.3" resolved "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz" integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw== @@ -622,6 +662,14 @@ resolved "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz" integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== +"@jridgewell/source-map@^0.3.3": + version "0.3.11" + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.11.tgz#b21835cbd36db656b857c2ad02ebd413cc13a9ba" + integrity sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA== + dependencies: + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + "@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": version "1.5.0" resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz" @@ -635,6 +683,11 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" +"@js-sdsl/ordered-map@^4.4.2": + version "4.4.2" + resolved "https://registry.yarnpkg.com/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz#9299f82874bab9e4c7f9c48d865becbfe8d6907c" + integrity sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw== + "@n1ru4l/json-patch-plus@^0.2.0": version "0.2.0" resolved "https://registry.npmjs.org/@n1ru4l/json-patch-plus/-/json-patch-plus-0.2.0.tgz" @@ -661,6 +714,64 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" +"@pkgjs/parseargs@^0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" + integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== + +"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" + integrity sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ== + +"@protobufjs/base64@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735" + integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg== + +"@protobufjs/codegen@^2.0.4": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@protobufjs/codegen/-/codegen-2.0.4.tgz#7ef37f0d010fb028ad1ad59722e506d9262815cb" + integrity sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg== + +"@protobufjs/eventemitter@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz#355cbc98bafad5978f9ed095f397621f1d066b70" + integrity sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q== + +"@protobufjs/fetch@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/fetch/-/fetch-1.1.0.tgz#ba99fb598614af65700c1619ff06d454b0d84c45" + integrity sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ== + dependencies: + "@protobufjs/aspromise" "^1.1.1" + "@protobufjs/inquire" "^1.1.0" + +"@protobufjs/float@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@protobufjs/float/-/float-1.0.2.tgz#5e9e1abdcb73fc0a7cb8b291df78c8cbd97b87d1" + integrity sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ== + +"@protobufjs/inquire@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/inquire/-/inquire-1.1.0.tgz#ff200e3e7cf2429e2dcafc1140828e8cc638f089" + integrity sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q== + +"@protobufjs/path@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/path/-/path-1.1.2.tgz#6cc2b20c5c9ad6ad0dccfd21ca7673d8d7fbf68d" + integrity sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA== + +"@protobufjs/pool@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/pool/-/pool-1.1.0.tgz#09fd15f2d6d3abfa9b65bc366506d6ad7846ff54" + integrity sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw== + +"@protobufjs/utf8@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" + integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw== + "@redis/bloom@1.2.0": version "1.2.0" resolved "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz" @@ -807,12 +918,22 @@ dependencies: bson "*" -"@types/dockerode@^2.5.34": - version "2.5.34" - resolved "https://registry.npmjs.org/@types/dockerode/-/dockerode-2.5.34.tgz" - integrity sha512-LcbLGcvcBwBAvjH9UrUI+4qotY+A5WCer5r43DR5XHv2ZIEByNXFdPLo1XxR+v/BjkGjlggW8qUiXuVEhqfkpA== +"@types/docker-modem@*": + version "3.0.6" + resolved "https://registry.yarnpkg.com/@types/docker-modem/-/docker-modem-3.0.6.tgz#1f9262fcf85425b158ca725699a03eb23cddbf87" + integrity sha512-yKpAGEuKRSS8wwx0joknWxsmLha78wNMe9R2S3UNsVOkZded8UqOrV8KoeDXoXsjndxwyF3eIhyClGbO1SEhEg== + dependencies: + "@types/node" "*" + "@types/ssh2" "*" + +"@types/dockerode@^3.3.47": + version "3.3.47" + resolved "https://registry.yarnpkg.com/@types/dockerode/-/dockerode-3.3.47.tgz#cf8c6b4efcd0bb28b0e6009e613e7faab1b96e75" + integrity sha512-ShM1mz7rCjdssXt7Xz0u1/R2BJC7piWa3SJpUBiVjCf2A3XNn4cP6pUVaD8bLanpPVVn4IKzJuw3dOvkJ8IbYw== dependencies: + "@types/docker-modem" "*" "@types/node" "*" + "@types/ssh2" "*" "@types/dotenv@^8.2.0": version "8.2.0" @@ -821,6 +942,27 @@ dependencies: dotenv "*" +"@types/eslint-scope@^3.7.7": + version "3.7.7" + resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.7.tgz#3108bd5f18b0cdb277c867b3dd449c9ed7079ac5" + integrity sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg== + dependencies: + "@types/eslint" "*" + "@types/estree" "*" + +"@types/eslint@*": + version "9.6.1" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-9.6.1.tgz#d5795ad732ce81715f27f75da913004a56751584" + integrity sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag== + dependencies: + "@types/estree" "*" + "@types/json-schema" "*" + +"@types/estree@*", "@types/estree@^1.0.8": + version "1.0.8" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e" + integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w== + "@types/graceful-fs@^4.1.3": version "4.1.9" resolved "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz" @@ -847,14 +989,19 @@ dependencies: "@types/istanbul-lib-report" "*" -"@types/jest@^29.2.3": +"@types/jest@^29.5.14": version "29.5.14" - resolved "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.14.tgz#2b910912fa1d6856cadcd0c1f95af7df1d6049e5" integrity sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ== dependencies: expect "^29.0.0" pretty-format "^29.0.0" +"@types/json-schema@*", "@types/json-schema@^7.0.15", "@types/json-schema@^7.0.9": + version "7.0.15" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== + "@types/json-schema@^7.0.7": version "7.0.9" resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz" @@ -873,13 +1020,20 @@ "@types/bson" "*" "@types/node" "*" -"@types/node@*", "@types/node@>=8.9.0", "@types/node@^18.11.9": +"@types/node@*", "@types/node@>=8.9.0": version "18.19.74" resolved "https://registry.npmjs.org/@types/node/-/node-18.19.74.tgz" integrity sha512-HMwEkkifei3L605gFdV+/UwtpxP6JSzM+xFk2Ia6DNFSwSVBRh9qp5Tgf4lNFOMfPVuU0WnkcWpXZpgn5ufO4A== dependencies: undici-types "~5.26.4" +"@types/node@>=13.7.0": + version "25.0.10" + resolved "https://registry.yarnpkg.com/@types/node/-/node-25.0.10.tgz#4864459c3c9459376b8b75fd051315071c8213e7" + integrity sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg== + dependencies: + undici-types "~7.16.0" + "@types/node@^12.7.2": version "12.20.28" resolved "https://registry.npmjs.org/@types/node/-/node-12.20.28.tgz" @@ -890,6 +1044,20 @@ resolved "https://registry.npmjs.org/@types/node/-/node-14.17.21.tgz" integrity sha512-zv8ukKci1mrILYiQOwGSV4FpkZhyxQtuFWGya2GujWg+zVAeRQ4qbaMmWp9vb9889CFA8JECH7lkwCL6Ygg8kA== +"@types/node@^18.11.18": + version "18.19.130" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.130.tgz#da4c6324793a79defb7a62cba3947ec5add00d59" + integrity sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg== + dependencies: + undici-types "~5.26.4" + +"@types/node@^20.17.0": + version "20.19.30" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.19.30.tgz#84fa87498ade5cd2b6ba8f8eec01d3b138ca60d0" + integrity sha512-WJtwWJu7UdlvzEAUm484QNg5eAoq5QR08KDNx7g45Usrs2NtOPiX8ugDqmKdXkyL03rBqU5dYNYVQetEpBHq2g== + dependencies: + undici-types "~6.21.0" + "@types/nodemailer@^6.4.0": version "6.4.4" resolved "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.4.tgz" @@ -904,6 +1072,28 @@ dependencies: "@types/node" "*" +"@types/ssh2-streams@*": + version "0.1.13" + resolved "https://registry.yarnpkg.com/@types/ssh2-streams/-/ssh2-streams-0.1.13.tgz#f8d34a22be50fb8dbafbb2bbc289add0d22daa51" + integrity sha512-faHyY3brO9oLEA0QlcO8N2wT7R0+1sHWZvQ+y3rMLwdY1ZyS1z0W3t65j9PqT4HmQ6ALzNe7RZlNuCNE0wBSWA== + dependencies: + "@types/node" "*" + +"@types/ssh2@*": + version "1.15.5" + resolved "https://registry.yarnpkg.com/@types/ssh2/-/ssh2-1.15.5.tgz#6d8f45db2f39519b8d9377268fa71ed77d969686" + integrity sha512-N1ASjp/nXH3ovBHddRJpli4ozpk6UdDYIX4RJWFa9L1YKnzdhTlVmiGHm4DZnj/jLbqZpes4aeR30EFGQtvhQQ== + dependencies: + "@types/node" "^18.11.18" + +"@types/ssh2@^0.5.48": + version "0.5.52" + resolved "https://registry.yarnpkg.com/@types/ssh2/-/ssh2-0.5.52.tgz#9dbd8084e2a976e551d5e5e70b978ed8b5965741" + integrity sha512-lbLLlXxdCZOSJMCInKH2+9V/77ET2J6NPQHpFI0kda61Dd1KglJs+fPQBchizmzYSOJBgdTajhPqBO1xxLywvg== + dependencies: + "@types/node" "*" + "@types/ssh2-streams" "*" + "@types/stack-utils@^2.0.0": version "2.0.3" resolved "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz" @@ -1006,149 +1196,125 @@ "@typescript-eslint/types" "4.33.0" eslint-visitor-keys "^2.0.0" -"@webassemblyjs/ast@1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz" - integrity sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA== - dependencies: - "@webassemblyjs/helper-module-context" "1.9.0" - "@webassemblyjs/helper-wasm-bytecode" "1.9.0" - "@webassemblyjs/wast-parser" "1.9.0" - -"@webassemblyjs/floating-point-hex-parser@1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz" - integrity sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA== - -"@webassemblyjs/helper-api-error@1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz" - integrity sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw== - -"@webassemblyjs/helper-buffer@1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz" - integrity sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA== - -"@webassemblyjs/helper-code-frame@1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz" - integrity sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA== - dependencies: - "@webassemblyjs/wast-printer" "1.9.0" - -"@webassemblyjs/helper-fsm@1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz" - integrity sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw== - -"@webassemblyjs/helper-module-context@1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz" - integrity sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g== - dependencies: - "@webassemblyjs/ast" "1.9.0" - -"@webassemblyjs/helper-wasm-bytecode@1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz" - integrity sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw== - -"@webassemblyjs/helper-wasm-section@1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz" - integrity sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-buffer" "1.9.0" - "@webassemblyjs/helper-wasm-bytecode" "1.9.0" - "@webassemblyjs/wasm-gen" "1.9.0" - -"@webassemblyjs/ieee754@1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz" - integrity sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg== +"@webassemblyjs/ast@1.14.1", "@webassemblyjs/ast@^1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.14.1.tgz#a9f6a07f2b03c95c8d38c4536a1fdfb521ff55b6" + integrity sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ== dependencies: - "@xtuc/ieee754" "^1.2.0" + "@webassemblyjs/helper-numbers" "1.13.2" + "@webassemblyjs/helper-wasm-bytecode" "1.13.2" + +"@webassemblyjs/floating-point-hex-parser@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz#fcca1eeddb1cc4e7b6eed4fc7956d6813b21b9fb" + integrity sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA== + +"@webassemblyjs/helper-api-error@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz#e0a16152248bc38daee76dd7e21f15c5ef3ab1e7" + integrity sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ== + +"@webassemblyjs/helper-buffer@1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz#822a9bc603166531f7d5df84e67b5bf99b72b96b" + integrity sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA== -"@webassemblyjs/leb128@1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.9.0.tgz" - integrity sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw== +"@webassemblyjs/helper-numbers@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz#dbd932548e7119f4b8a7877fd5a8d20e63490b2d" + integrity sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA== dependencies: + "@webassemblyjs/floating-point-hex-parser" "1.13.2" + "@webassemblyjs/helper-api-error" "1.13.2" "@xtuc/long" "4.2.2" -"@webassemblyjs/utf8@1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.9.0.tgz" - integrity sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w== - -"@webassemblyjs/wasm-edit@1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz" - integrity sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-buffer" "1.9.0" - "@webassemblyjs/helper-wasm-bytecode" "1.9.0" - "@webassemblyjs/helper-wasm-section" "1.9.0" - "@webassemblyjs/wasm-gen" "1.9.0" - "@webassemblyjs/wasm-opt" "1.9.0" - "@webassemblyjs/wasm-parser" "1.9.0" - "@webassemblyjs/wast-printer" "1.9.0" - -"@webassemblyjs/wasm-gen@1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz" - integrity sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-wasm-bytecode" "1.9.0" - "@webassemblyjs/ieee754" "1.9.0" - "@webassemblyjs/leb128" "1.9.0" - "@webassemblyjs/utf8" "1.9.0" - -"@webassemblyjs/wasm-opt@1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz" - integrity sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-buffer" "1.9.0" - "@webassemblyjs/wasm-gen" "1.9.0" - "@webassemblyjs/wasm-parser" "1.9.0" - -"@webassemblyjs/wasm-parser@1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz" - integrity sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-api-error" "1.9.0" - "@webassemblyjs/helper-wasm-bytecode" "1.9.0" - "@webassemblyjs/ieee754" "1.9.0" - "@webassemblyjs/leb128" "1.9.0" - "@webassemblyjs/utf8" "1.9.0" - -"@webassemblyjs/wast-parser@1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz" - integrity sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/floating-point-hex-parser" "1.9.0" - "@webassemblyjs/helper-api-error" "1.9.0" - "@webassemblyjs/helper-code-frame" "1.9.0" - "@webassemblyjs/helper-fsm" "1.9.0" +"@webassemblyjs/helper-wasm-bytecode@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz#e556108758f448aae84c850e593ce18a0eb31e0b" + integrity sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA== + +"@webassemblyjs/helper-wasm-section@1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz#9629dda9c4430eab54b591053d6dc6f3ba050348" + integrity sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw== + dependencies: + "@webassemblyjs/ast" "1.14.1" + "@webassemblyjs/helper-buffer" "1.14.1" + "@webassemblyjs/helper-wasm-bytecode" "1.13.2" + "@webassemblyjs/wasm-gen" "1.14.1" + +"@webassemblyjs/ieee754@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz#1c5eaace1d606ada2c7fd7045ea9356c59ee0dba" + integrity sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw== + dependencies: + "@xtuc/ieee754" "^1.2.0" + +"@webassemblyjs/leb128@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.13.2.tgz#57c5c3deb0105d02ce25fa3fd74f4ebc9fd0bbb0" + integrity sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw== + dependencies: "@xtuc/long" "4.2.2" -"@webassemblyjs/wast-printer@1.9.0": - version "1.9.0" - resolved "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz" - integrity sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA== +"@webassemblyjs/utf8@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.13.2.tgz#917a20e93f71ad5602966c2d685ae0c6c21f60f1" + integrity sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ== + +"@webassemblyjs/wasm-edit@^1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz#ac6689f502219b59198ddec42dcd496b1004d597" + integrity sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ== + dependencies: + "@webassemblyjs/ast" "1.14.1" + "@webassemblyjs/helper-buffer" "1.14.1" + "@webassemblyjs/helper-wasm-bytecode" "1.13.2" + "@webassemblyjs/helper-wasm-section" "1.14.1" + "@webassemblyjs/wasm-gen" "1.14.1" + "@webassemblyjs/wasm-opt" "1.14.1" + "@webassemblyjs/wasm-parser" "1.14.1" + "@webassemblyjs/wast-printer" "1.14.1" + +"@webassemblyjs/wasm-gen@1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz#991e7f0c090cb0bb62bbac882076e3d219da9570" + integrity sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg== + dependencies: + "@webassemblyjs/ast" "1.14.1" + "@webassemblyjs/helper-wasm-bytecode" "1.13.2" + "@webassemblyjs/ieee754" "1.13.2" + "@webassemblyjs/leb128" "1.13.2" + "@webassemblyjs/utf8" "1.13.2" + +"@webassemblyjs/wasm-opt@1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz#e6f71ed7ccae46781c206017d3c14c50efa8106b" + integrity sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw== + dependencies: + "@webassemblyjs/ast" "1.14.1" + "@webassemblyjs/helper-buffer" "1.14.1" + "@webassemblyjs/wasm-gen" "1.14.1" + "@webassemblyjs/wasm-parser" "1.14.1" + +"@webassemblyjs/wasm-parser@1.14.1", "@webassemblyjs/wasm-parser@^1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz#b3e13f1893605ca78b52c68e54cf6a865f90b9fb" + integrity sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ== + dependencies: + "@webassemblyjs/ast" "1.14.1" + "@webassemblyjs/helper-api-error" "1.13.2" + "@webassemblyjs/helper-wasm-bytecode" "1.13.2" + "@webassemblyjs/ieee754" "1.13.2" + "@webassemblyjs/leb128" "1.13.2" + "@webassemblyjs/utf8" "1.13.2" + +"@webassemblyjs/wast-printer@1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz#3bb3e9638a8ae5fdaf9610e7a06b4d9f9aa6fe07" + integrity sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw== dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/wast-parser" "1.9.0" + "@webassemblyjs/ast" "1.14.1" "@xtuc/long" "4.2.2" "@xtuc/ieee754@^1.2.0": @@ -1166,21 +1332,33 @@ abbrev@1: resolved "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== +abort-controller@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" + integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== + dependencies: + event-target-shim "^5.0.0" + +acorn-import-phases@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz#16eb850ba99a056cb7cbfe872ffb8972e18c8bd7" + integrity sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ== + acorn-jsx@^5.3.1: version "5.3.2" resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== -acorn@^6.4.1: - version "6.4.2" - resolved "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz" - integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== - acorn@^7.4.0: version "7.4.1" resolved "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== +acorn@^8.15.0: + version "8.15.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.15.0.tgz#a360898bc415edaac46c8241f6383975b930b816" + integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg== + agent-base@6: version "6.0.2" resolved "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz" @@ -1188,17 +1366,21 @@ agent-base@6: dependencies: debug "4" -ajv-errors@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz" - integrity sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ== +ajv-formats@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520" + integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA== + dependencies: + ajv "^8.0.0" -ajv-keywords@^3.1.0, ajv-keywords@^3.4.1: - version "3.5.2" - resolved "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz" - integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== +ajv-keywords@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz#69d4d385a4733cdbeab44964a1170a88f87f0e16" + integrity sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw== + dependencies: + fast-deep-equal "^3.1.3" -ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.4: +ajv@^6.10.0, ajv@^6.12.4: version "6.12.6" resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -1208,6 +1390,16 @@ ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.4: json-schema-traverse "^0.4.1" uri-js "^4.2.2" +ajv@^8.0.0, ajv@^8.9.0: + version "8.17.1" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6" + integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g== + dependencies: + fast-deep-equal "^3.1.3" + fast-uri "^3.0.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + ajv@^8.0.1: version "8.6.3" resolved "https://registry.npmjs.org/ajv/-/ajv-8.6.3.tgz" @@ -1269,6 +1461,11 @@ ansi-regex@^5.0.1: resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== +ansi-regex@^6.0.1: + version "6.2.2" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.2.2.tgz#60216eea464d864597ce2832000738a0589650c1" + integrity sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg== + ansi-styles@^3.2.0, ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz" @@ -1288,18 +1485,10 @@ ansi-styles@^5.0.0: resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz" integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== -any-promise@^1.1.0: - version "1.3.0" - resolved "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz" - integrity sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A== - -anymatch@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz" - integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw== - dependencies: - micromatch "^3.1.4" - normalize-path "^2.1.1" +ansi-styles@^6.1.0: + version "6.2.3" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.3.tgz#c044d5dcc521a076413472597a1acb1f103c4041" + integrity sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg== anymatch@^3.0.3, anymatch@~3.1.1, anymatch@~3.1.2: version "3.1.2" @@ -1309,10 +1498,31 @@ anymatch@^3.0.3, anymatch@~3.1.1, anymatch@~3.1.2: normalize-path "^3.0.0" picomatch "^2.0.4" -aproba@^1.1.1: - version "1.2.0" - resolved "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz" - integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== +archiver-utils@^5.0.0, archiver-utils@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/archiver-utils/-/archiver-utils-5.0.2.tgz#63bc719d951803efc72cf961a56ef810760dd14d" + integrity sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA== + dependencies: + glob "^10.0.0" + graceful-fs "^4.2.0" + is-stream "^2.0.1" + lazystream "^1.0.0" + lodash "^4.17.15" + normalize-path "^3.0.0" + readable-stream "^4.0.0" + +archiver@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/archiver/-/archiver-7.0.1.tgz#c9d91c350362040b8927379c7aa69c0655122f61" + integrity sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ== + dependencies: + archiver-utils "^5.0.2" + async "^3.2.4" + buffer-crc32 "^1.0.0" + readable-stream "^4.0.0" + readdir-glob "^1.1.2" + tar-stream "^3.0.0" + zip-stream "^6.0.1" arg@^4.1.0: version "4.1.3" @@ -1326,21 +1536,6 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" -arr-diff@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz" - integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= - -arr-flatten@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz" - integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== - -arr-union@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz" - integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= - array-includes@^3.1.1: version "3.1.4" resolved "https://registry.npmjs.org/array-includes/-/array-includes-3.1.4.tgz" @@ -1357,11 +1552,6 @@ array-union@^2.1.0: resolved "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== -array-unique@^0.3.2: - version "0.3.2" - resolved "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz" - integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= - array.prototype.flat@^1.2.3: version "1.2.5" resolved "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz" @@ -1371,16 +1561,6 @@ array.prototype.flat@^1.2.3: define-properties "^1.1.3" es-abstract "^1.19.0" -asn1.js@^5.2.0: - version "5.4.1" - resolved "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz" - integrity sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA== - dependencies: - bn.js "^4.0.0" - inherits "^2.0.1" - minimalistic-assert "^1.0.0" - safer-buffer "^2.1.0" - asn1@^0.2.6: version "0.2.6" resolved "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz" @@ -1388,37 +1568,24 @@ asn1@^0.2.6: dependencies: safer-buffer "~2.1.0" -assert@^1.1.1: - version "1.5.0" - resolved "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz" - integrity sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA== - dependencies: - object-assign "^4.1.1" - util "0.10.3" - -assign-symbols@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz" - integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= - astral-regex@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz" integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== -async-each@^1.0.1: - version "1.0.3" - resolved "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz" - integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== +async-lock@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/async-lock/-/async-lock-1.4.1.tgz#56b8718915a9b68b10fce2f2a9a3dddf765ef53f" + integrity sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ== async@^3.1.0: version "3.2.1" resolved "https://registry.npmjs.org/async/-/async-3.2.1.tgz" integrity sha512-XdD5lRO/87udXCMC9meWdYiR+Nq6ZjUfXidViUZGu2F1MO4T3XwZ1et0hb2++BgLfhyJwy44BGB/yx80ABx8hg== -async@^3.2.3: +async@^3.2.4: version "3.2.6" - resolved "https://registry.npmjs.org/async/-/async-3.2.6.tgz" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.6.tgz#1b0728e14929d51b85b449b7f06e27c1145e38ce" integrity sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA== at-least-node@^1.0.0: @@ -1426,11 +1593,6 @@ at-least-node@^1.0.0: resolved "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz" integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== -atob@^2.1.2: - version "2.1.2" - resolved "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz" - integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== - axios@^0.20.0: version "0.20.0" resolved "https://registry.npmjs.org/axios/-/axios-0.20.0.tgz" @@ -1445,6 +1607,11 @@ axios@^0.21.1, axios@^0.21.2: dependencies: follow-redirects "^1.14.0" +b4a@^1.6.4: + version "1.7.3" + resolved "https://registry.yarnpkg.com/b4a/-/b4a-1.7.3.tgz#24cf7ccda28f5465b66aec2bac69e32809bf112f" + integrity sha512-5Q2mfq2WfGuFp3uS//0s6baOJLMoVduPYVeNmDYxu5OUA1/cBfvr2RIS7vi62LdNj/urk1hfmj867I3qt6uZ7Q== + babel-jest@^29.7.0: version "29.7.0" resolved "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz" @@ -1513,23 +1680,57 @@ balanced-match@^1.0.0: resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== -base64-js@^1.0.2, base64-js@^1.3.1: +bare-events@^2.5.4, bare-events@^2.7.0: + version "2.8.2" + resolved "https://registry.yarnpkg.com/bare-events/-/bare-events-2.8.2.tgz#7b3e10bd8e1fc80daf38bb516921678f566ab89f" + integrity sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ== + +bare-fs@^4.0.1: + version "4.5.3" + resolved "https://registry.yarnpkg.com/bare-fs/-/bare-fs-4.5.3.tgz#316cce9bcec66a5e49fb24ec2743f6b846a2f35c" + integrity sha512-9+kwVx8QYvt3hPWnmb19tPnh38c6Nihz8Lx3t0g9+4GoIf3/fTgYwM4Z6NxgI+B9elLQA7mLE9PpqcWtOMRDiQ== + dependencies: + bare-events "^2.5.4" + bare-path "^3.0.0" + bare-stream "^2.6.4" + bare-url "^2.2.2" + fast-fifo "^1.3.2" + +bare-os@^3.0.1: + version "3.6.2" + resolved "https://registry.yarnpkg.com/bare-os/-/bare-os-3.6.2.tgz#b3c4f5ad5e322c0fd0f3c29fc97d19009e2796e5" + integrity sha512-T+V1+1srU2qYNBmJCXZkUY5vQ0B4FSlL3QDROnKQYOqeiQR8UbjNHlPa+TIbM4cuidiN9GaTaOZgSEgsvPbh5A== + +bare-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/bare-path/-/bare-path-3.0.0.tgz#b59d18130ba52a6af9276db3e96a2e3d3ea52178" + integrity sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw== + dependencies: + bare-os "^3.0.1" + +bare-stream@^2.6.4: + version "2.7.0" + resolved "https://registry.yarnpkg.com/bare-stream/-/bare-stream-2.7.0.tgz#5b9e7dd0a354d06e82d6460c426728536c35d789" + integrity sha512-oyXQNicV1y8nc2aKffH+BUHFRXmx6VrPzlnaEvMhram0nPBrKcEdcyBg5r08D0i8VxngHFAiVyn1QKXpSG0B8A== + dependencies: + streamx "^2.21.0" + +bare-url@^2.2.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/bare-url/-/bare-url-2.3.2.tgz#4aef382efa662b2180a6fe4ca07a71b39bdf7ca3" + integrity sha512-ZMq4gd9ngV5aTMa5p9+UfY0b3skwhHELaDkhEHetMdX0LRkW9kzaym4oo/Eh+Ghm0CCDuMTsRIGM/ytUc1ZYmw== + dependencies: + bare-path "^3.0.0" + +base64-js@^1.3.1: version "1.5.1" resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== -base@^0.11.1: - version "0.11.2" - resolved "https://registry.npmjs.org/base/-/base-0.11.2.tgz" - integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== - dependencies: - cache-base "^1.0.1" - class-utils "^0.3.5" - component-emitter "^1.2.1" - define-property "^1.0.0" - isobject "^3.0.1" - mixin-deep "^1.2.0" - pascalcase "^0.1.1" +baseline-browser-mapping@^2.9.0: + version "2.9.18" + resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.9.18.tgz#c8281693035a9261b10d662a5379650a6c2d1ff7" + integrity sha512-e23vBV1ZLfjb9apvfPk4rHVu2ry6RIr2Wfs+O324okSidrX7pTAnEJPCh/O5BtRlr7QtZI7ktOP3vsqr7Z5XoA== bcrypt-pbkdf@^1.0.2: version "1.0.2" @@ -1538,28 +1739,11 @@ bcrypt-pbkdf@^1.0.2: dependencies: tweetnacl "^0.14.3" -big.js@^5.2.2: - version "5.2.2" - resolved "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz" - integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== - -binary-extensions@^1.0.0: - version "1.13.1" - resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz" - integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== - binary-extensions@^2.0.0: version "2.2.0" resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== -bindings@^1.5.0: - version "1.5.0" - resolved "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz" - integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== - dependencies: - file-uri-to-path "1.0.0" - bintrees@1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/bintrees/-/bintrees-1.0.1.tgz" @@ -1591,21 +1775,11 @@ bl@^4.0.3: inherits "^2.0.4" readable-stream "^3.4.0" -bluebird@^3.5.5, bluebird@^3.7.2: +bluebird@^3.7.2: version "3.7.2" resolved "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== -bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.9: - version "4.12.0" - resolved "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz" - integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== - -bn.js@^5.0.0, bn.js@^5.1.1: - version "5.2.0" - resolved "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz" - integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw== - boxen@^5.0.0: version "5.1.2" resolved "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz" @@ -1635,22 +1809,6 @@ brace-expansion@^2.0.1: dependencies: balanced-match "^1.0.0" -braces@^2.3.1, braces@^2.3.2: - version "2.3.2" - resolved "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz" - integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== - dependencies: - arr-flatten "^1.1.0" - array-unique "^0.3.2" - extend-shallow "^2.0.1" - fill-range "^4.0.0" - isobject "^3.0.1" - repeat-element "^1.1.2" - snapdragon "^0.8.1" - snapdragon-node "^2.0.1" - split-string "^3.0.2" - to-regex "^3.0.1" - braces@^3.0.1, braces@~3.0.2: version "3.0.2" resolved "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz" @@ -1658,77 +1816,11 @@ braces@^3.0.1, braces@~3.0.2: dependencies: fill-range "^7.0.1" -brorand@^1.0.1, brorand@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz" - integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= - browser-stdout@1.3.1: version "1.3.1" resolved "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz" integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== -browserify-aes@^1.0.0, browserify-aes@^1.0.4: - version "1.2.0" - resolved "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz" - integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== - dependencies: - buffer-xor "^1.0.3" - cipher-base "^1.0.0" - create-hash "^1.1.0" - evp_bytestokey "^1.0.3" - inherits "^2.0.1" - safe-buffer "^5.0.1" - -browserify-cipher@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz" - integrity sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w== - dependencies: - browserify-aes "^1.0.4" - browserify-des "^1.0.0" - evp_bytestokey "^1.0.0" - -browserify-des@^1.0.0: - version "1.0.2" - resolved "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz" - integrity sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A== - dependencies: - cipher-base "^1.0.1" - des.js "^1.0.0" - inherits "^2.0.1" - safe-buffer "^5.1.2" - -browserify-rsa@^4.0.0, browserify-rsa@^4.0.1: - version "4.1.0" - resolved "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz" - integrity sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog== - dependencies: - bn.js "^5.0.0" - randombytes "^2.0.1" - -browserify-sign@^4.0.0: - version "4.2.1" - resolved "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz" - integrity sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg== - dependencies: - bn.js "^5.1.1" - browserify-rsa "^4.0.1" - create-hash "^1.2.0" - create-hmac "^1.1.7" - elliptic "^6.5.3" - inherits "^2.0.4" - parse-asn1 "^5.1.5" - readable-stream "^3.6.0" - safe-buffer "^5.2.0" - -browserify-zlib@^0.2.0: - version "0.2.0" - resolved "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz" - integrity sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA== - dependencies: - pako "~1.0.5" - browserslist@^4.24.0: version "4.24.4" resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz" @@ -1739,6 +1831,17 @@ browserslist@^4.24.0: node-releases "^2.0.19" update-browserslist-db "^1.1.1" +browserslist@^4.28.1: + version "4.28.1" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.28.1.tgz#7f534594628c53c63101079e27e40de490456a95" + integrity sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA== + dependencies: + baseline-browser-mapping "^2.9.0" + caniuse-lite "^1.0.30001759" + electron-to-chromium "^1.5.263" + node-releases "^2.0.27" + update-browserslist-db "^1.2.0" + bs-logger@^0.2.6: version "0.2.6" resolved "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz" @@ -1765,6 +1868,16 @@ bson@^1.1.4: resolved "https://registry.npmjs.org/bson/-/bson-1.1.6.tgz" integrity sha512-EvVNVeGo4tHxwi8L6bPj3y3itEvStdwvvlojVxxbyYfoaxJ6keLgrTuKdyfEAszFK+H3olzBuafE0yoh0D1gdg== +bson@^7.0.0: + version "7.1.1" + resolved "https://registry.yarnpkg.com/bson/-/bson-7.1.1.tgz#19965d9138e1c4d88e4690414d91c84f217c84e8" + integrity sha512-TtJgBB+QyOlWjrbM+8bRgH84VM/xrDjyBFgSgGrfZF4xvt6gbEDtcswm27Tn9F9TWsjQybxT8b8VpCP/oJK4Dw== + +buffer-crc32@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-1.0.0.tgz#a10993b9055081d55304bd9feb4a072de179f405" + integrity sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w== + buffer-crc32@~0.2.3: version "0.2.13" resolved "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz" @@ -1780,20 +1893,6 @@ buffer-more-ints@~1.0.0: resolved "https://registry.npmjs.org/buffer-more-ints/-/buffer-more-ints-1.0.0.tgz" integrity sha512-EMetuGFz5SLsT0QTnXzINh4Ksr+oo4i+UGTXEshiGCQWnsgSs7ZhJ8fzlwQ+OzEMs0MpDAMr1hxnblp5a4vcHg== -buffer-xor@^1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz" - integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= - -buffer@^4.3.0: - version "4.9.2" - resolved "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz" - integrity sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg== - dependencies: - base64-js "^1.0.2" - ieee754 "^1.1.4" - isarray "^1.0.0" - buffer@^5.5.0, buffer@^5.6.0: version "5.7.1" resolved "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz" @@ -1802,57 +1901,24 @@ buffer@^5.5.0, buffer@^5.6.0: base64-js "^1.3.1" ieee754 "^1.1.13" +buffer@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + buildcheck@~0.0.6: version "0.0.6" resolved "https://registry.npmjs.org/buildcheck/-/buildcheck-0.0.6.tgz" integrity sha512-8f9ZJCUXyT1M35Jx7MkBgmBMo3oHTTBIPLiY9xyL0pl3T5RwcPEY8cUHr5LBNfu/fk6c2T4DJZuVM/8ZZT2D2A== -builtin-status-codes@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz" - integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug= - byline@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz" integrity sha512-s6webAy+R4SR8XVuJWt2V2rGvhnrhxN+9S15GNuTK3wKPOXFF6RNc+8ug2XhH+2s4f+uudG4kUVYmYOQWL2g0Q== -cacache@^12.0.2: - version "12.0.4" - resolved "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz" - integrity sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ== - dependencies: - bluebird "^3.5.5" - chownr "^1.1.1" - figgy-pudding "^3.5.1" - glob "^7.1.4" - graceful-fs "^4.1.15" - infer-owner "^1.0.3" - lru-cache "^5.1.1" - mississippi "^3.0.0" - mkdirp "^0.5.1" - move-concurrently "^1.0.1" - promise-inflight "^1.0.1" - rimraf "^2.6.3" - ssri "^6.0.1" - unique-filename "^1.1.1" - y18n "^4.0.0" - -cache-base@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz" - integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== - dependencies: - collection-visit "^1.0.0" - component-emitter "^1.2.1" - get-value "^2.0.6" - has-value "^1.0.0" - isobject "^3.0.1" - set-value "^2.0.0" - to-object-path "^0.3.0" - union-value "^1.0.0" - unset-value "^1.0.0" - cacheable-request@^6.0.0: version "6.1.0" resolved "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz" @@ -1899,6 +1965,11 @@ caniuse-lite@^1.0.30001688: resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001695.tgz" integrity sha512-vHyLade6wTgI2u1ec3WQBxv+2BrTERV28UXQu9LO6lZ9pYeMk34vjXFLOxo1A4UBA8XTL4njRQZdno/yYaSmWw== +caniuse-lite@^1.0.30001759: + version "1.0.30001766" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001766.tgz#b6f6b55cb25a2d888d9393104d14751c6a7d6f7a" + integrity sha512-4C0lfJ0/YPjJQHagaE9x2Elb69CIqEPZeG0anQt9SIvIoOH4a4uaRl73IavyO+0qZh6MDLH//DrXThEYKHkmYA== + chalk@^2.0.0, chalk@^2.4.2: version "2.4.2" resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" @@ -1908,7 +1979,7 @@ chalk@^2.0.0, chalk@^2.4.2: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0: +chalk@^4.0.0, chalk@^4.1.0: version "4.1.2" resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -1936,26 +2007,7 @@ chokidar@3.3.0: optionalDependencies: fsevents "~2.1.1" -chokidar@^2.1.8: - version "2.1.8" - resolved "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz" - integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg== - dependencies: - anymatch "^2.0.0" - async-each "^1.0.1" - braces "^2.3.2" - glob-parent "^3.1.0" - inherits "^2.0.3" - is-binary-path "^1.0.0" - is-glob "^4.0.0" - normalize-path "^3.0.0" - path-is-absolute "^1.0.0" - readdirp "^2.2.1" - upath "^1.1.1" - optionalDependencies: - fsevents "^1.2.7" - -chokidar@^3.2.2, chokidar@^3.4.1: +chokidar@^3.2.2: version "3.5.2" resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz" integrity sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ== @@ -1990,29 +2042,11 @@ ci-info@^3.2.0: resolved "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz" integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== -cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: - version "1.0.4" - resolved "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz" - integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== - dependencies: - inherits "^2.0.1" - safe-buffer "^5.0.1" - cjs-module-lexer@^1.0.0: version "1.4.1" resolved "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.1.tgz" integrity sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA== -class-utils@^0.3.5: - version "0.3.6" - resolved "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz" - integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== - dependencies: - arr-union "^3.1.0" - define-property "^0.2.5" - isobject "^3.0.0" - static-extend "^0.1.1" - cli-boxes@^2.2.1: version "2.2.1" resolved "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz" @@ -2090,14 +2124,6 @@ collect-v8-coverage@^1.0.0: resolved "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz" integrity sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg== -collection-visit@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz" - integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= - dependencies: - map-visit "^1.0.0" - object-visit "^1.0.0" - color-convert@^1.9.0, color-convert@^1.9.1: version "1.9.3" resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz" @@ -2171,26 +2197,22 @@ commondir@^1.0.1: resolved "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz" integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= -component-emitter@^1.2.1: - version "1.3.0" - resolved "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz" - integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== +compress-commons@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/compress-commons/-/compress-commons-6.0.2.tgz#26d31251a66b9d6ba23a84064ecd3a6a71d2609e" + integrity sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg== + dependencies: + crc-32 "^1.2.0" + crc32-stream "^6.0.0" + is-stream "^2.0.1" + normalize-path "^3.0.0" + readable-stream "^4.0.0" concat-map@0.0.1: version "0.0.1" resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= -concat-stream@^1.5.0: - version "1.6.2" - resolved "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz" - integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== - dependencies: - buffer-from "^1.0.0" - inherits "^2.0.3" - readable-stream "^2.2.2" - typedarray "^0.0.6" - configstore@^5.0.1: version "5.0.1" resolved "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz" @@ -2203,16 +2225,6 @@ configstore@^5.0.1: write-file-atomic "^3.0.0" xdg-basedir "^4.0.0" -console-browserify@^1.1.0: - version "1.2.0" - resolved "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz" - integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA== - -constants-browserify@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz" - integrity sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U= - contains-path@^0.1.0: version "0.1.0" resolved "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz" @@ -2223,23 +2235,6 @@ convert-source-map@^2.0.0: resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz" integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== -copy-concurrently@^1.0.0: - version "1.0.5" - resolved "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz" - integrity sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A== - dependencies: - aproba "^1.1.1" - fs-write-stream-atomic "^1.0.8" - iferr "^0.1.5" - mkdirp "^0.5.1" - rimraf "^2.5.4" - run-queue "^1.0.0" - -copy-descriptor@^0.1.0: - version "0.1.1" - resolved "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz" - integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= - core-util-is@~1.0.0: version "1.0.3" resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz" @@ -2253,36 +2248,18 @@ cpu-features@~0.0.10: buildcheck "~0.0.6" nan "^2.19.0" -create-ecdh@^4.0.0: - version "4.0.4" - resolved "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz" - integrity sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A== - dependencies: - bn.js "^4.1.0" - elliptic "^6.5.3" +crc-32@^1.2.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.2.tgz#3cad35a934b8bf71f25ca524b6da51fb7eace2ff" + integrity sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ== -create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz" - integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== - dependencies: - cipher-base "^1.0.1" - inherits "^2.0.1" - md5.js "^1.3.4" - ripemd160 "^2.0.1" - sha.js "^2.4.0" - -create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: - version "1.1.7" - resolved "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz" - integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== - dependencies: - cipher-base "^1.0.3" - create-hash "^1.1.0" - inherits "^2.0.1" - ripemd160 "^2.0.0" - safe-buffer "^5.0.1" - sha.js "^2.4.8" +crc32-stream@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/crc32-stream/-/crc32-stream-6.0.0.tgz#8529a3868f8b27abb915f6c3617c0fadedbf9430" + integrity sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g== + dependencies: + crc-32 "^1.2.0" + readable-stream "^4.0.0" create-jest@^29.7.0: version "29.7.0" @@ -2297,7 +2274,7 @@ create-jest@^29.7.0: jest-util "^29.7.0" prompts "^2.0.1" -cross-spawn@^7.0.2, cross-spawn@^7.0.3: +cross-spawn@^7.0.2, cross-spawn@^7.0.3, cross-spawn@^7.0.6: version "7.0.6" resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz" integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== @@ -2306,33 +2283,11 @@ cross-spawn@^7.0.2, cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" -crypto-browserify@^3.11.0: - version "3.12.0" - resolved "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz" - integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg== - dependencies: - browserify-cipher "^1.0.0" - browserify-sign "^4.0.0" - create-ecdh "^4.0.0" - create-hash "^1.1.0" - create-hmac "^1.1.0" - diffie-hellman "^5.0.0" - inherits "^2.0.1" - pbkdf2 "^3.0.3" - public-encrypt "^4.0.0" - randombytes "^2.0.0" - randomfill "^1.0.3" - crypto-random-string@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz" integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA== -cyclist@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz" - integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk= - date-fns@2.15.0: version "2.15.0" resolved "https://registry.npmjs.org/date-fns/-/date-fns-2.15.0.tgz" @@ -2352,7 +2307,7 @@ debug@4, debug@4.3.2, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: dependencies: ms "2.1.2" -debug@^2.2.0, debug@^2.3.3, debug@^2.6.9, debug@~2.6.9: +debug@^2.2.0, debug@^2.6.9, debug@~2.6.9: version "2.6.9" resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== @@ -2373,16 +2328,18 @@ debug@^4.2.0: dependencies: ms "^2.1.3" +debug@^4.4.3: + version "4.4.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" + integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== + dependencies: + ms "^2.1.3" + decamelize@^1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= -decode-uri-component@^0.2.0: - version "0.2.0" - resolved "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz" - integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= - decompress-response@^3.3.0: version "3.3.0" resolved "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz" @@ -2422,41 +2379,11 @@ define-properties@^1.1.2, define-properties@^1.1.3: dependencies: object-keys "^1.0.12" -define-property@^0.2.5: - version "0.2.5" - resolved "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz" - integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= - dependencies: - is-descriptor "^0.1.0" - -define-property@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz" - integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY= - dependencies: - is-descriptor "^1.0.0" - -define-property@^2.0.2: - version "2.0.2" - resolved "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz" - integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== - dependencies: - is-descriptor "^1.0.2" - isobject "^3.0.1" - denque@^1.4.1: version "1.5.1" resolved "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz" integrity sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw== -des.js@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz" - integrity sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA== - dependencies: - inherits "^2.0.1" - minimalistic-assert "^1.0.0" - detect-newline@^3.0.0: version "3.1.0" resolved "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz" @@ -2477,15 +2404,6 @@ diff@^4.0.1: resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== -diffie-hellman@^5.0.0: - version "5.0.3" - resolved "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz" - integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg== - dependencies: - bn.js "^4.1.0" - miller-rabin "^4.0.0" - randombytes "^2.0.0" - dir-glob@^3.0.1: version "3.0.1" resolved "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz" @@ -2493,31 +2411,35 @@ dir-glob@^3.0.1: dependencies: path-type "^4.0.0" -docker-compose@^0.23.5: - version "0.23.19" - resolved "https://registry.npmjs.org/docker-compose/-/docker-compose-0.23.19.tgz" - integrity sha512-v5vNLIdUqwj4my80wxFDkNH+4S85zsRuH29SO7dCWVWPCMt/ohZBsGN6g6KXWifT0pzQ7uOxqEKCYCDPJ8Vz4g== +docker-compose@^1.3.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/docker-compose/-/docker-compose-1.3.1.tgz#92928c4e564fb6eefc311ef1fea75afe9dcbc20b" + integrity sha512-rF0wH69G3CCcmkN9J1RVMQBaKe8o77LT/3XmqcLIltWWVxcWAzp2TnO7wS3n/umZHN3/EVrlT3exSBMal+Ou1w== dependencies: - yaml "^1.10.2" + yaml "^2.2.2" -docker-modem@^3.0.0: - version "3.0.8" - resolved "https://registry.npmjs.org/docker-modem/-/docker-modem-3.0.8.tgz" - integrity sha512-f0ReSURdM3pcKPNS30mxOHSbaFLcknGmQjwSfmbcdOw1XWKXVhukM3NJHhr7NpY9BIyyWQb0EBo3KQvvuU5egQ== +docker-modem@^5.0.6: + version "5.0.6" + resolved "https://registry.yarnpkg.com/docker-modem/-/docker-modem-5.0.6.tgz#cbe9d86a1fe66d7a072ac7fb99a9fc390a3e8b9a" + integrity sha512-ens7BiayssQz/uAxGzH8zGXCtiV24rRWXdjNha5V4zSOcxmAZsfGVm/PPFbwQdqEkDnhG+SyR9E3zSHUbOKXBQ== dependencies: debug "^4.1.1" readable-stream "^3.5.0" split-ca "^1.0.1" - ssh2 "^1.11.0" + ssh2 "^1.15.0" -dockerode@^3.2.1: - version "3.3.5" - resolved "https://registry.npmjs.org/dockerode/-/dockerode-3.3.5.tgz" - integrity sha512-/0YNa3ZDNeLr/tSckmD69+Gq+qVNhvKfAHNeZJBnp7EOP6RGKV8ORrJHkUn20So5wU+xxT7+1n5u8PjHbfjbSA== +dockerode@^4.0.9: + version "4.0.9" + resolved "https://registry.yarnpkg.com/dockerode/-/dockerode-4.0.9.tgz#15b32000edad25520be6fafa9ad6bc4529b06be7" + integrity sha512-iND4mcOWhPaCNh54WmK/KoSb35AFqPAUWFMffTQcp52uQt36b5uNwEJTSXntJZBbeGad72Crbi/hvDIv6us/6Q== dependencies: "@balena/dockerignore" "^1.0.2" - docker-modem "^3.0.0" - tar-fs "~2.0.1" + "@grpc/grpc-js" "^1.11.1" + "@grpc/proto-loader" "^0.7.13" + docker-modem "^5.0.6" + protobufjs "^7.3.2" + tar-fs "^2.1.4" + uuid "^10.0.0" doctrine@1.5.0: version "1.5.0" @@ -2534,11 +2456,6 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" -domain-browser@^1.1.1: - version "1.2.0" - resolved "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz" - integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA== - dot-prop@^5.2.0: version "5.3.0" resolved "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz" @@ -2561,41 +2478,21 @@ duplexer3@^0.1.4: resolved "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz" integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= -duplexify@^3.4.2, duplexify@^3.6.0: - version "3.7.1" - resolved "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz" - integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g== - dependencies: - end-of-stream "^1.0.0" - inherits "^2.0.1" - readable-stream "^2.0.0" - stream-shift "^1.0.0" +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== -ejs@^3.1.10: - version "3.1.10" - resolved "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz" - integrity sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA== - dependencies: - jake "^10.8.5" +electron-to-chromium@^1.5.263: + version "1.5.279" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.279.tgz#67dfdeb22fd81412d0d18d1d9b2c749e9b8945cb" + integrity sha512-0bblUU5UNdOt5G7XqGiJtpZMONma6WAfq9vsFmtn9x1+joAObr6x1chfqyxFSDCAFwFhCQDrqeAr6MYdpwJ9Hg== electron-to-chromium@^1.5.73: version "1.5.88" resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.88.tgz" integrity sha512-K3C2qf1o+bGzbilTDCTBhTQcMS9KW60yTAaTeeXsfvQuTDDwlokLam/AdqlqcSy9u4UainDgsHV23ksXAOgamw== -elliptic@^6.5.3: - version "6.5.4" - resolved "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz" - integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== - dependencies: - bn.js "^4.11.9" - brorand "^1.1.0" - hash.js "^1.0.0" - hmac-drbg "^1.0.1" - inherits "^2.0.4" - minimalistic-assert "^1.0.1" - minimalistic-crypto-utils "^1.0.1" - emittery@^0.13.1: version "0.13.1" resolved "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz" @@ -2611,31 +2508,30 @@ emoji-regex@^8.0.0: resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== -emojis-list@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz" - integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== enabled@2.0.x: version "2.0.0" resolved "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz" integrity sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ== -end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1: +end-of-stream@^1.1.0, end-of-stream@^1.4.1: 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== dependencies: once "^1.4.0" -enhanced-resolve@^4.5.0: - version "4.5.0" - resolved "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz" - integrity sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg== +enhanced-resolve@^5.17.4: + version "5.18.4" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.18.4.tgz#c22d33055f3952035ce6a144ce092447c525f828" + integrity sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q== dependencies: - graceful-fs "^4.1.2" - memory-fs "^0.5.0" - tapable "^1.0.0" + graceful-fs "^4.2.4" + tapable "^2.2.0" enquirer@^2.3.5: version "2.3.6" @@ -2644,13 +2540,6 @@ enquirer@^2.3.5: dependencies: ansi-colors "^4.1.1" -errno@^0.1.3, errno@~0.1.7: - version "0.1.8" - resolved "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz" - integrity sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A== - dependencies: - prr "~1.0.1" - error-ex@^1.2.0, error-ex@^1.3.1: version "1.3.2" resolved "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz" @@ -2684,6 +2573,11 @@ es-abstract@^1.19.0, es-abstract@^1.19.1: string.prototype.trimstart "^1.0.4" unbox-primitive "^1.0.1" +es-module-lexer@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-2.0.0.tgz#f657cd7a9448dcdda9c070a3cb75e5dc1e85f5b1" + integrity sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw== + es-to-primitive@^1.2.1: version "1.2.1" resolved "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz" @@ -2820,15 +2714,7 @@ eslint-plugin-standard@4.0.2: resolved "https://registry.npmjs.org/eslint-plugin-standard/-/eslint-plugin-standard-4.0.2.tgz" integrity sha512-nKptN8l7jksXkwFk++PhJB3cCDTcXOEyhISIN86Ue2feJ1LFyY3PrY3/xT2keXlJSY5bpmbiTG0f885/YKAvTA== -eslint-scope@^4.0.3: - version "4.0.3" - resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz" - integrity sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg== - dependencies: - esrecurse "^4.1.0" - estraverse "^4.1.1" - -eslint-scope@^5.1.1: +eslint-scope@5.1.1, eslint-scope@^5.1.1: version "5.1.1" resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz" integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== @@ -2927,7 +2813,7 @@ esquery@^1.4.0: dependencies: estraverse "^5.1.0" -esrecurse@^4.1.0, esrecurse@^4.3.0: +esrecurse@^4.3.0: version "4.3.0" resolved "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz" integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== @@ -2949,18 +2835,22 @@ esutils@^2.0.2: resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== -events@^3.0.0: - version "3.3.0" - resolved "https://registry.npmjs.org/events/-/events-3.3.0.tgz" - integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== +event-target-shim@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" + integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== -evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz" - integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== +events-universal@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/events-universal/-/events-universal-1.0.1.tgz#b56a84fd611b6610e0a2d0f09f80fdf931e2dfe6" + integrity sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw== dependencies: - md5.js "^1.3.4" - safe-buffer "^5.1.1" + bare-events "^2.7.0" + +events@^3.2.0, events@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== execa@^5.0.0: version "5.1.1" @@ -2982,19 +2872,6 @@ exit@^0.1.2: resolved "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz" integrity sha1-BjJjj42HfMghB9MKD/8aF8uhzQw= -expand-brackets@^2.1.4: - version "2.1.4" - resolved "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz" - integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI= - dependencies: - debug "^2.3.3" - define-property "^0.2.5" - extend-shallow "^2.0.1" - posix-character-classes "^0.1.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - expect@^29.0.0, expect@^29.7.0: version "29.7.0" resolved "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz" @@ -3006,40 +2883,16 @@ expect@^29.0.0, expect@^29.7.0: jest-message-util "^29.7.0" jest-util "^29.7.0" -extend-shallow@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz" - integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= - dependencies: - is-extendable "^0.1.0" - -extend-shallow@^3.0.0, extend-shallow@^3.0.2: - version "3.0.2" - resolved "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz" - integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg= - dependencies: - assign-symbols "^1.0.0" - is-extendable "^1.0.1" - -extglob@^2.0.4: - version "2.0.4" - resolved "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz" - integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== - dependencies: - array-unique "^0.3.2" - define-property "^1.0.0" - expand-brackets "^2.1.4" - extend-shallow "^2.0.1" - fragment-cache "^0.2.1" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== +fast-fifo@^1.2.0, fast-fifo@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/fast-fifo/-/fast-fifo-1.3.2.tgz#286e31de96eb96d38a97899815740ba2a4f3640c" + integrity sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ== + fast-glob@^3.1.1: version "3.2.7" resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz" @@ -3061,6 +2914,11 @@ fast-levenshtein@^2.0.6: resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= +fast-uri@^3.0.1: + version "3.1.0" + resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.1.0.tgz#66eecff6c764c0df9b762e62ca7edcfb53b4edfa" + integrity sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA== + fastq@^1.6.0: version "1.13.0" resolved "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz" @@ -3087,11 +2945,6 @@ fecha@^4.2.0: resolved "https://registry.npmjs.org/fecha/-/fecha-4.2.1.tgz" integrity sha512-MMMQ0ludy/nBs1/o0zVOiKTpG7qMbonKUzjJgQFEuvq6INZ1OraKPRAWkBq5vlKLOUMpmNYG1JoN3oDPUQ9m3Q== -figgy-pudding@^3.5.1: - version "3.5.2" - resolved "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz" - integrity sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw== - file-entry-cache@^6.0.1: version "6.0.1" resolved "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz" @@ -3099,28 +2952,6 @@ file-entry-cache@^6.0.1: dependencies: flat-cache "^3.0.4" -file-uri-to-path@1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz" - integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== - -filelist@^1.0.4: - 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== - dependencies: - minimatch "^5.0.1" - -fill-range@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz" - integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= - dependencies: - extend-shallow "^2.0.1" - is-number "^3.0.0" - repeat-string "^1.6.1" - to-regex-range "^2.1.0" - fill-range@^7.0.1: version "7.0.1" resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz" @@ -3128,15 +2959,6 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" -find-cache-dir@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz" - integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ== - dependencies: - commondir "^1.0.1" - make-dir "^2.0.0" - pkg-dir "^3.0.0" - find-cache-dir@^3.3.1: version "3.3.2" resolved "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz" @@ -3193,14 +3015,6 @@ flatted@^3.1.0: resolved "https://registry.npmjs.org/flatted/-/flatted-3.2.2.tgz" integrity sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA== -flush-write-stream@^1.0.0: - version "1.1.1" - resolved "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz" - integrity sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w== - dependencies: - inherits "^2.0.3" - readable-stream "^2.3.6" - fn-args@5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/fn-args/-/fn-args-5.0.0.tgz" @@ -3221,30 +3035,18 @@ follow-redirects@^1.10.0, follow-redirects@^1.14.0: resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz" integrity sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA== -for-in@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz" - integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= - foreachasync@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/foreachasync/-/foreachasync-3.0.0.tgz" integrity sha1-VQKYfchxS+M5IJfzLgBxyd7gfPY= -fragment-cache@^0.2.1: - version "0.2.1" - resolved "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz" - integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= - dependencies: - map-cache "^0.2.2" - -from2@^2.1.0: - version "2.3.0" - resolved "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz" - integrity sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8= +foreground-child@^3.1.0: + version "3.3.1" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.1.tgz#32e8e9ed1b68a3497befb9ac2b6adf92a638576f" + integrity sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw== dependencies: - inherits "^2.0.1" - readable-stream "^2.0.0" + cross-spawn "^7.0.6" + signal-exit "^4.0.1" fs-constants@^1.0.0: version "1.0.0" @@ -3261,29 +3063,11 @@ fs-extra@9.0.1: jsonfile "^6.0.1" universalify "^1.0.0" -fs-write-stream-atomic@^1.0.8: - version "1.0.10" - resolved "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz" - integrity sha1-tH31NJPvkR33VzHnCp3tAYnbQMk= - dependencies: - graceful-fs "^4.1.2" - iferr "^0.1.5" - imurmurhash "^0.1.4" - readable-stream "1 || 2" - fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= -fsevents@^1.2.7: - version "1.2.13" - resolved "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz" - integrity sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw== - dependencies: - bindings "^1.5.0" - nan "^2.12.1" - fsevents@^2.3.2: version "2.3.3" resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz" @@ -3351,6 +3135,11 @@ get-port@^5.1.1: resolved "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz" integrity sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ== +get-port@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/get-port/-/get-port-7.1.0.tgz#d5a500ebfc7aa705294ec2b83cc38c5d0e364fec" + integrity sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw== + get-stream@^4.1.0: version "4.1.0" resolved "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz" @@ -3378,19 +3167,6 @@ get-symbol-description@^1.0.0: call-bind "^1.0.2" get-intrinsic "^1.1.1" -get-value@^2.0.3, get-value@^2.0.6: - version "2.0.6" - resolved "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz" - integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= - -glob-parent@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz" - integrity sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4= - dependencies: - is-glob "^3.1.0" - path-dirname "^1.0.0" - glob-parent@^5.1.2, glob-parent@~5.1.0, glob-parent@~5.1.2: version "5.1.2" resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" @@ -3398,6 +3174,11 @@ glob-parent@^5.1.2, glob-parent@~5.1.0, glob-parent@~5.1.2: dependencies: is-glob "^4.0.1" +glob-to-regexp@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" + integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== + glob@7.1.3: version "7.1.3" resolved "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz" @@ -3410,6 +3191,18 @@ glob@7.1.3: once "^1.3.0" path-is-absolute "^1.0.0" +glob@^10.0.0: + version "10.5.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.5.0.tgz#8ec0355919cd3338c28428a23d4f24ecc5fe738c" + integrity sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg== + dependencies: + foreground-child "^3.1.0" + jackspeak "^3.1.2" + minimatch "^9.0.4" + minipass "^7.1.2" + package-json-from-dist "^1.0.0" + path-scurry "^1.11.1" + glob@^7.1.3, glob@^7.1.4: version "7.2.0" resolved "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz" @@ -3422,18 +3215,6 @@ glob@^7.1.3, glob@^7.1.4: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.1.6: - 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" - glob@^9.2.0: version "9.3.5" resolved "https://registry.npmjs.org/glob/-/glob-9.3.5.tgz" @@ -3492,7 +3273,7 @@ got@^9.6.0: to-readable-stream "^1.0.0" url-parse-lax "^3.0.0" -graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.9: +graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.11, graceful-fs@^4.2.4, graceful-fs@^4.2.9: version "4.2.11" resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== @@ -3502,6 +3283,18 @@ growl@1.10.5: resolved "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz" integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== +handlebars@^4.7.8: + version "4.7.8" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.8.tgz#41c42c18b1be2365439188c77c6afae71c0cd9e9" + integrity sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ== + dependencies: + minimist "^1.2.5" + neo-async "^2.6.2" + source-map "^0.6.1" + wordwrap "^1.0.0" + optionalDependencies: + uglify-js "^3.1.4" + has-bigints@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz" @@ -3529,37 +3322,6 @@ has-tostringtag@^1.0.0: dependencies: has-symbols "^1.0.2" -has-value@^0.3.1: - version "0.3.1" - resolved "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz" - integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= - dependencies: - get-value "^2.0.3" - has-values "^0.1.4" - isobject "^2.0.0" - -has-value@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz" - integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= - dependencies: - get-value "^2.0.6" - has-values "^1.0.0" - isobject "^3.0.0" - -has-values@^0.1.4: - version "0.1.4" - resolved "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz" - integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E= - -has-values@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz" - integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= - dependencies: - is-number "^3.0.0" - kind-of "^4.0.0" - has-yarn@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz" @@ -3572,37 +3334,11 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" -hash-base@^3.0.0: - version "3.1.0" - resolved "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz" - integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA== - dependencies: - inherits "^2.0.4" - readable-stream "^3.6.0" - safe-buffer "^5.2.0" - -hash.js@^1.0.0, hash.js@^1.0.3: - version "1.1.7" - resolved "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz" - integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== - dependencies: - inherits "^2.0.3" - minimalistic-assert "^1.0.1" - he@1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/he/-/he-1.2.0.tgz" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== -hmac-drbg@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz" - integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= - dependencies: - hash.js "^1.0.3" - minimalistic-assert "^1.0.0" - minimalistic-crypto-utils "^1.0.1" - hosted-git-info@^2.1.4: version "2.8.9" resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz" @@ -3618,11 +3354,6 @@ http-cache-semantics@^4.0.0: resolved "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz" integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== -https-browserify@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz" - integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= - https-proxy-agent@^5.0.0: version "5.0.1" resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz" @@ -3636,16 +3367,11 @@ human-signals@^2.1.0: resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== -ieee754@^1.1.13, ieee754@^1.1.4: +ieee754@^1.1.13, ieee754@^1.2.1: version "1.2.1" resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== -iferr@^0.1.5: - version "0.1.5" - resolved "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz" - integrity sha1-xg7taebY/bazEEofy8ocGS3FtQE= - ignore-by-default@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz" @@ -3687,11 +3413,6 @@ imurmurhash@^0.1.4: resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz" integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= -infer-owner@^1.0.3: - version "1.0.4" - resolved "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz" - integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A== - inflight@^1.0.4: version "1.0.6" resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" @@ -3700,21 +3421,11 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3: +inherits@2, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3: version "2.0.4" resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -inherits@2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" - integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE= - -inherits@2.0.3: - version "2.0.3" - resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" - integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= - ini@2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz" @@ -3734,20 +3445,6 @@ internal-slot@^1.0.3: has "^1.0.3" side-channel "^1.0.4" -is-accessor-descriptor@^0.1.6: - version "0.1.6" - resolved "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz" - integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= - dependencies: - kind-of "^3.0.2" - -is-accessor-descriptor@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz" - integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== - dependencies: - kind-of "^6.0.0" - is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz" @@ -3765,13 +3462,6 @@ is-bigint@^1.0.1: dependencies: has-bigints "^1.0.1" -is-binary-path@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz" - integrity sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg= - dependencies: - binary-extensions "^1.0.0" - is-binary-path@~2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz" @@ -3787,11 +3477,6 @@ is-boolean-object@^1.1.0: call-bind "^1.0.2" has-tostringtag "^1.0.0" -is-buffer@^1.1.5: - version "1.1.6" - resolved "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz" - integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== - is-buffer@~2.0.3: version "2.0.5" resolved "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz" @@ -3816,20 +3501,6 @@ is-core-module@^2.2.0: dependencies: has "^1.0.3" -is-data-descriptor@^0.1.4: - version "0.1.4" - resolved "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz" - integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= - dependencies: - kind-of "^3.0.2" - -is-data-descriptor@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz" - integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== - dependencies: - kind-of "^6.0.0" - is-date-object@^1.0.1: version "1.0.5" resolved "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz" @@ -3837,37 +3508,7 @@ is-date-object@^1.0.1: dependencies: has-tostringtag "^1.0.0" -is-descriptor@^0.1.0: - version "0.1.6" - resolved "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz" - integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== - dependencies: - is-accessor-descriptor "^0.1.6" - is-data-descriptor "^0.1.4" - kind-of "^5.0.0" - -is-descriptor@^1.0.0, is-descriptor@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz" - integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== - dependencies: - is-accessor-descriptor "^1.0.0" - is-data-descriptor "^1.0.0" - kind-of "^6.0.2" - -is-extendable@^0.1.0, is-extendable@^0.1.1: - version "0.1.1" - resolved "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz" - integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= - -is-extendable@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz" - integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== - dependencies: - is-plain-object "^2.0.4" - -is-extglob@^2.1.0, is-extglob@^2.1.1: +is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= @@ -3887,13 +3528,6 @@ is-generator-fn@^2.0.0: resolved "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== -is-glob@^3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz" - integrity sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo= - dependencies: - is-extglob "^2.1.0" - is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: version "4.0.3" resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz" @@ -3926,13 +3560,6 @@ is-number-object@^1.0.4: dependencies: has-tostringtag "^1.0.0" -is-number@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz" - integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= - dependencies: - kind-of "^3.0.2" - is-number@^7.0.0: version "7.0.0" resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" @@ -3948,13 +3575,6 @@ is-path-inside@^3.0.2: resolved "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz" integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== -is-plain-object@^2.0.3, is-plain-object@^2.0.4: - version "2.0.4" - resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz" - integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== - dependencies: - isobject "^3.0.1" - is-regex@^1.1.4: version "1.1.4" resolved "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz" @@ -3968,7 +3588,7 @@ is-shared-array-buffer@^1.0.1: resolved "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz" integrity sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA== -is-stream@^2.0.0: +is-stream@^2.0.0, is-stream@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz" integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== @@ -3999,16 +3619,6 @@ is-weakref@^1.0.1: dependencies: call-bind "^1.0.0" -is-windows@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz" - integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== - -is-wsl@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz" - integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= - is-yarn-global@^0.3.0: version "0.3.0" resolved "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz" @@ -4019,7 +3629,7 @@ isarray@0.0.1: resolved "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= -isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: +isarray@^1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= @@ -4029,18 +3639,6 @@ isexe@^2.0.0: resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= -isobject@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz" - integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= - dependencies: - isarray "1.0.0" - -isobject@^3.0.0, isobject@^3.0.1: - version "3.0.1" - resolved "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz" - integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= - istanbul-lib-coverage@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz" @@ -4099,15 +3697,14 @@ istanbul-reports@^3.1.3: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" -jake@^10.8.5: - version "10.9.2" - resolved "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz" - integrity sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA== +jackspeak@^3.1.2: + version "3.4.3" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a" + integrity sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw== dependencies: - async "^3.2.3" - chalk "^4.0.2" - filelist "^1.0.4" - minimatch "^3.1.2" + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" jest-changed-files@^29.7.0: version "29.7.0" @@ -4286,15 +3883,6 @@ jest-message-util@^29.7.0: slash "^3.0.0" stack-utils "^2.0.3" -jest-mock@29.2.0: - version "29.2.0" - resolved "https://registry.npmjs.org/jest-mock/-/jest-mock-29.2.0.tgz" - integrity sha512-aiWGR0P8ivssIO17xkehLGFtCcef2ZwQFNPwEer1jQLHxPctDlIg3Hs6QMq1KpPz5dkCcgM7mwGif4a9IPznlg== - dependencies: - "@jest/types" "^29.2.0" - "@types/node" "*" - jest-util "^29.2.0" - jest-mock@^29.7.0: version "29.7.0" resolved "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz" @@ -4418,7 +4006,7 @@ jest-snapshot@^29.7.0: pretty-format "^29.7.0" semver "^7.5.3" -jest-util@^29.0.0, jest-util@^29.2.0, jest-util@^29.7.0: +jest-util@^29.7.0: version "29.7.0" resolved "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz" integrity sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA== @@ -4456,6 +4044,15 @@ jest-watcher@^29.7.0: jest-util "^29.7.0" string-length "^4.0.1" +jest-worker@^27.4.5: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" + integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^8.0.0" + jest-worker@^29.7.0: version "29.7.0" resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz" @@ -4466,9 +4063,9 @@ jest-worker@^29.7.0: merge-stream "^2.0.0" supports-color "^8.0.0" -jest@^29.2.2: +jest@^29.7.0: version "29.7.0" - resolved "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz" + resolved "https://registry.yarnpkg.com/jest/-/jest-29.7.0.tgz#994676fc24177f088f1c5e3737f5697204ff2613" integrity sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw== dependencies: "@jest/core" "^29.7.0" @@ -4512,12 +4109,7 @@ json-buffer@3.0.0: resolved "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz" integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= -json-parse-better-errors@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz" - integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== - -json-parse-even-better-errors@^2.3.0: +json-parse-even-better-errors@^2.3.0, json-parse-even-better-errors@^2.3.1: version "2.3.1" resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz" integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== @@ -4570,30 +4162,6 @@ keyv@^3.0.0: dependencies: json-buffer "3.0.0" -kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: - version "3.2.2" - resolved "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz" - integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= - dependencies: - is-buffer "^1.1.5" - -kind-of@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz" - integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc= - dependencies: - is-buffer "^1.1.5" - -kind-of@^5.0.0: - version "5.1.0" - resolved "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz" - integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== - -kind-of@^6.0.0, kind-of@^6.0.2: - version "6.0.3" - resolved "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz" - integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== - kleur@^3.0.3: version "3.0.3" resolved "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz" @@ -4611,6 +4179,13 @@ latest-version@^5.1.0: dependencies: package-json "^6.3.0" +lazystream@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.1.tgz#494c831062f1f9408251ec44db1cba29242a2638" + integrity sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw== + dependencies: + readable-stream "^2.0.5" + leven@^3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz" @@ -4639,19 +4214,10 @@ load-json-file@^2.0.0: pify "^2.0.0" strip-bom "^3.0.0" -loader-runner@^2.4.0: - version "2.4.0" - resolved "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz" - integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw== - -loader-utils@^1.2.3: - version "1.4.0" - resolved "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz" - integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA== - dependencies: - big.js "^5.2.2" - emojis-list "^3.0.0" - json5 "^1.0.1" +loader-runner@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.1.tgz#6c76ed29b0ccce9af379208299f07f876de737e3" + integrity sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q== locate-path@^2.0.0: version "2.0.0" @@ -4693,6 +4259,11 @@ lodash-es@^4.17.11: resolved "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz" integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw== +lodash.camelcase@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" + integrity sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA== + lodash.clonedeep@^4.5.0: version "4.5.0" resolved "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz" @@ -4776,6 +4347,11 @@ logform@^2.2.0: safe-stable-stringify "^1.1.0" triple-beam "^1.3.0" +long@^5.0.0: + version "5.3.2" + resolved "https://registry.yarnpkg.com/long/-/long-5.3.2.tgz#1d84463095999262d7d7b7f8bfd4a8cc55167f83" + integrity sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA== + lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz" @@ -4813,14 +4389,6 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" -make-dir@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz" - integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== - dependencies: - pify "^4.0.1" - semver "^5.6.0" - make-dir@^3.0.0, make-dir@^3.0.2: version "3.1.0" resolved "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz" @@ -4840,48 +4408,11 @@ makeerror@1.0.12: dependencies: tmpl "1.0.5" -map-cache@^0.2.2: - version "0.2.2" - resolved "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz" - integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= - -map-visit@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz" - integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= - dependencies: - object-visit "^1.0.0" - md5-file@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/md5-file/-/md5-file-5.0.0.tgz" integrity sha512-xbEFXCYVWrSx/gEKS1VPlg84h/4L20znVIulKw6kMfmBUAZNAnF00eczz9ICMl+/hjQGo5KSXRxbL/47X3rmMw== -md5.js@^1.3.4: - version "1.3.5" - resolved "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz" - integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== - dependencies: - hash-base "^3.0.0" - inherits "^2.0.1" - safe-buffer "^5.1.2" - -memory-fs@^0.4.1: - version "0.4.1" - resolved "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz" - integrity sha1-OpoguEYlI+RHz7x+i7gO1me/xVI= - dependencies: - errno "^0.1.3" - readable-stream "^2.0.1" - -memory-fs@^0.5.0: - version "0.5.0" - resolved "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz" - integrity sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA== - dependencies: - errno "^0.1.3" - readable-stream "^2.0.1" - memory-pager@^1.0.2: version "1.5.0" resolved "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz" @@ -4897,25 +4428,6 @@ merge2@^1.3.0: resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== -micromatch@^3.1.10, micromatch@^3.1.4: - version "3.1.10" - resolved "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz" - integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== - dependencies: - arr-diff "^4.0.0" - array-unique "^0.3.2" - braces "^2.3.1" - define-property "^2.0.2" - extend-shallow "^3.0.2" - extglob "^2.0.4" - fragment-cache "^0.2.1" - kind-of "^6.0.2" - nanomatch "^1.2.9" - object.pick "^1.3.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.2" - micromatch@^4.0.4: version "4.0.4" resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz" @@ -4938,13 +4450,17 @@ migrate-mongo@^7.2.1: mongodb "3.5.9" p-each-series "2.1.0" -miller-rabin@^4.0.0: - version "4.0.1" - resolved "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz" - integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA== +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.27: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== dependencies: - bn.js "^4.0.0" - brorand "^1.0.1" + mime-db "1.52.0" mimic-fn@^2.1.0: version "2.1.0" @@ -4956,16 +4472,6 @@ mimic-response@^1.0.0, mimic-response@^1.0.1: resolved "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz" integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== -minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz" - integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== - -minimalistic-crypto-utils@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz" - integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= - minimatch@3.0.4, minimatch@3.0.x, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz" @@ -4973,16 +4479,9 @@ minimatch@3.0.4, minimatch@3.0.x, minimatch@^3.0.4: dependencies: brace-expansion "^1.1.7" -minimatch@^3.1.1, minimatch@^3.1.2: - version "3.1.2" - resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" - integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== - dependencies: - brace-expansion "^1.1.7" - -minimatch@^5.0.1: +minimatch@^5.1.0: version "5.1.6" - resolved "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== dependencies: brace-expansion "^2.0.1" @@ -4994,6 +4493,13 @@ minimatch@^8.0.2: dependencies: brace-expansion "^2.0.1" +minimatch@^9.0.4: + version "9.0.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" + integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== + dependencies: + brace-expansion "^2.0.1" + minimist@^1.2.0, minimist@^1.2.5: version "1.2.5" resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz" @@ -5004,41 +4510,17 @@ minipass@^4.2.4: resolved "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz" integrity sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ== -"minipass@^5.0.0 || ^6.0.2 || ^7.0.0": +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2: version "7.1.2" resolved "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz" integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== -mississippi@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz" - integrity sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA== - dependencies: - concat-stream "^1.5.0" - duplexify "^3.4.2" - end-of-stream "^1.1.0" - flush-write-stream "^1.0.0" - from2 "^2.1.0" - parallel-transform "^1.1.0" - pump "^3.0.0" - pumpify "^1.3.3" - stream-each "^1.1.0" - through2 "^2.0.0" - -mixin-deep@^1.2.0: - version "1.3.2" - resolved "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz" - integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== - dependencies: - for-in "^1.0.2" - is-extendable "^1.0.1" - mkdirp-classic@^0.5.2: version "0.5.3" resolved "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz" integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== -mkdirp@0.5.5, mkdirp@^0.5.1, mkdirp@^0.5.3: +mkdirp@0.5.5: version "0.5.5" resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz" integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== @@ -5155,18 +4637,6 @@ mongodb@^3.6.2: optionalDependencies: saslprep "^1.0.0" -move-concurrently@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz" - integrity sha1-viwAX9oy4LKa8fBdfEszIUxwH5I= - dependencies: - aproba "^1.1.1" - copy-concurrently "^1.0.0" - fs-write-stream-atomic "^1.0.8" - mkdirp "^0.5.1" - rimraf "^2.5.4" - run-queue "^1.0.3" - ms@2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" @@ -5187,41 +4657,29 @@ ms@^2.1.1, ms@^2.1.3: resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== -nan@^2.12.1, nan@^2.18.0, nan@^2.19.0, nan@^2.20.0: +nan@^2.18.0, nan@^2.19.0: version "2.24.0" resolved "https://registry.npmjs.org/nan/-/nan-2.24.0.tgz" integrity sha512-Vpf9qnVW1RaDkoNKFUvfxqAbtI8ncb8OJlqZ9wwpXzWPEsvsB1nvdUi6oYrHIkQ1Y/tMDnr1h4nczS0VB9Xykg== +nan@^2.23.0: + version "2.25.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.25.0.tgz#937ed345e63d9481362a7942d49c4860d27eeabd" + integrity sha512-0M90Ag7Xn5KMLLZ7zliPWP3rT90P6PN+IzVFS0VqmnPktBk3700xUVv8Ikm9EUaUE5SDWdp/BIxdENzVznpm1g== + nanoid@^3.1.31: version "3.1.31" resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.1.31.tgz" integrity sha512-ZivnJm0o9bb13p2Ot5CpgC2rQdzB9Uxm/mFZweqm5eMViqOJe3PV6LU2E30SiLgheesmcPrjquqraoolONSA0A== -nanomatch@^1.2.9: - version "1.2.13" - resolved "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz" - integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== - dependencies: - arr-diff "^4.0.0" - array-unique "^0.3.2" - define-property "^2.0.2" - extend-shallow "^3.0.2" - fragment-cache "^0.2.1" - is-windows "^1.0.2" - kind-of "^6.0.2" - object.pick "^1.3.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= -neo-async@^2.5.0, neo-async@^2.6.1: +neo-async@^2.6.2: version "2.6.2" - resolved "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== node-cache@^5.1.2: @@ -5231,11 +4689,6 @@ node-cache@^5.1.2: dependencies: clone "2.x" -node-duration@^1.0.4: - version "1.0.4" - resolved "https://registry.npmjs.org/node-duration/-/node-duration-1.0.4.tgz" - integrity sha512-eUXYNSY7DL53vqfTosggWkvyIW3bhAcqBDIlolgNYlZhianXTrCL50rlUJWD1eRqkIxMppXTfiFbp+9SjpPrgA== - node-environment-flags@1.0.6: version "1.0.6" resolved "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.6.tgz" @@ -5254,40 +4707,16 @@ node-int64@^0.4.0: resolved "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz" integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs= -node-libs-browser@^2.2.1: - version "2.2.1" - resolved "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz" - integrity sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q== - dependencies: - assert "^1.1.1" - browserify-zlib "^0.2.0" - buffer "^4.3.0" - console-browserify "^1.1.0" - constants-browserify "^1.0.0" - crypto-browserify "^3.11.0" - domain-browser "^1.1.1" - events "^3.0.0" - https-browserify "^1.0.0" - os-browserify "^0.3.0" - path-browserify "0.0.1" - process "^0.11.10" - punycode "^1.2.4" - querystring-es3 "^0.2.0" - readable-stream "^2.3.3" - stream-browserify "^2.0.1" - stream-http "^2.7.2" - string_decoder "^1.0.0" - timers-browserify "^2.0.4" - tty-browserify "0.0.0" - url "^0.11.0" - util "^0.11.0" - vm-browserify "^1.0.1" - node-releases@^2.0.19: version "2.0.19" resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz" integrity sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw== +node-releases@^2.0.27: + version "2.0.27" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.27.tgz#eedca519205cf20f650f61d56b070db111231e4e" + integrity sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA== + nodemailer@^6.6.1: version "6.9.0" resolved "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.0.tgz" @@ -5326,13 +4755,6 @@ normalize-package-data@^2.3.2: semver "2 || 3 || 4 || 5" validate-npm-package-license "^3.0.1" -normalize-path@^2.1.1: - version "2.1.1" - resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz" - integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= - dependencies: - remove-trailing-separator "^1.0.1" - normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz" @@ -5350,20 +4772,11 @@ npm-run-path@^4.0.1: dependencies: path-key "^3.0.0" -object-assign@^4.1.0, object-assign@^4.1.1: +object-assign@^4.1.0: version "4.1.1" resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= -object-copy@^0.1.0: - version "0.1.0" - resolved "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz" - integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw= - dependencies: - copy-descriptor "^0.1.0" - define-property "^0.2.5" - kind-of "^3.0.3" - object-inspect@^1.11.0, object-inspect@^1.9.0: version "1.11.0" resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz" @@ -5374,13 +4787,6 @@ object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1: resolved "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== -object-visit@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz" - integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= - dependencies: - isobject "^3.0.0" - object.assign@4.1.0: version "4.1.0" resolved "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz" @@ -5410,13 +4816,6 @@ object.getownpropertydescriptors@^2.0.3: define-properties "^1.1.3" es-abstract "^1.19.1" -object.pick@^1.3.0: - version "1.3.0" - resolved "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz" - integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= - dependencies: - isobject "^3.0.1" - object.values@^1.1.1: version "1.1.5" resolved "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz" @@ -5471,11 +4870,6 @@ optionator@^0.9.1: type-check "^0.4.0" word-wrap "^1.2.3" -os-browserify@^0.3.0: - version "0.3.0" - resolved "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz" - integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc= - os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" @@ -5543,6 +4937,11 @@ p-try@^2.0.0: resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== +package-json-from-dist@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505" + integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== + package-json@^6.3.0: version "6.5.0" resolved "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz" @@ -5553,20 +4952,6 @@ package-json@^6.3.0: registry-url "^5.0.0" semver "^6.2.0" -pako@~1.0.5: - version "1.0.11" - resolved "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz" - integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== - -parallel-transform@^1.1.0: - version "1.2.0" - resolved "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz" - integrity sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg== - dependencies: - cyclist "^1.0.1" - inherits "^2.0.3" - readable-stream "^2.1.5" - parent-module@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz" @@ -5574,17 +4959,6 @@ parent-module@^1.0.0: dependencies: callsites "^3.0.0" -parse-asn1@^5.0.0, parse-asn1@^5.1.5: - version "5.1.6" - resolved "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz" - integrity sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw== - dependencies: - asn1.js "^5.2.0" - browserify-aes "^1.0.0" - evp_bytestokey "^1.0.0" - pbkdf2 "^3.0.3" - safe-buffer "^5.1.1" - parse-json@^2.2.0: version "2.2.0" resolved "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz" @@ -5602,21 +4976,6 @@ parse-json@^5.2.0: json-parse-even-better-errors "^2.3.0" lines-and-columns "^1.1.6" -pascalcase@^0.1.1: - version "0.1.1" - resolved "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz" - integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= - -path-browserify@0.0.1: - version "0.0.1" - resolved "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz" - integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ== - -path-dirname@^1.0.0: - version "1.0.2" - resolved "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz" - integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA= - path-exists@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz" @@ -5642,7 +5001,7 @@ path-parse@^1.0.6: resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== -path-scurry@^1.6.1: +path-scurry@^1.11.1, path-scurry@^1.6.1: version "1.11.1" resolved "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz" integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== @@ -5662,17 +5021,6 @@ path-type@^4.0.0: resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== -pbkdf2@^3.0.3: - version "3.1.2" - resolved "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz" - integrity sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA== - dependencies: - create-hash "^1.1.2" - create-hmac "^1.1.4" - ripemd160 "^2.0.1" - safe-buffer "^5.0.1" - sha.js "^2.4.8" - pend@~1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz" @@ -5693,11 +5041,6 @@ pify@^2.0.0: resolved "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz" integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= -pify@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz" - integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== - pirates@^4.0.4: version "4.0.6" resolved "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz" @@ -5710,13 +5053,6 @@ pkg-dir@^2.0.0: dependencies: find-up "^2.1.0" -pkg-dir@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz" - integrity sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw== - dependencies: - find-up "^3.0.0" - pkg-dir@^4.1.0, pkg-dir@^4.2.0: version "4.2.0" resolved "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz" @@ -5724,11 +5060,6 @@ pkg-dir@^4.1.0, pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" -posix-character-classes@^0.1.0: - version "0.1.1" - resolved "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz" - integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= - prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz" @@ -5784,11 +5115,6 @@ prometheus-gc-stats@^0.6.2: optionalDependencies: gc-stats "^1.4.0" -promise-inflight@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz" - integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM= - prompts@^2.0.1: version "2.4.2" resolved "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz" @@ -5797,15 +5123,44 @@ prompts@^2.0.1: kleur "^3.0.3" sisteransi "^1.0.5" +proper-lockfile@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/proper-lockfile/-/proper-lockfile-4.1.2.tgz#c8b9de2af6b2f1601067f98e01ac66baa223141f" + integrity sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA== + dependencies: + graceful-fs "^4.2.4" + retry "^0.12.0" + signal-exit "^3.0.2" + +properties-reader@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/properties-reader/-/properties-reader-2.3.0.tgz#f3ab84224c9535a7a36e011ae489a79a13b472b2" + integrity sha512-z597WicA7nDZxK12kZqHr2TcvwNU1GCfA5UwfDY/HDp3hXPoPlb5rlEx9bwGTiJnc0OqbBTkU975jDToth8Gxw== + dependencies: + mkdirp "^1.0.4" + property-expr@^2.0.2: version "2.0.4" resolved "https://registry.npmjs.org/property-expr/-/property-expr-2.0.4.tgz" integrity sha512-sFPkHQjVKheDNnPvotjQmm3KD3uk1fWKUN7CrpdbwmUx3CrG3QiM8QpTSimvig5vTXmTvjz7+TDvXOI9+4rkcg== -prr@~1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz" - integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY= +protobufjs@^7.2.5, protobufjs@^7.3.2, protobufjs@^7.5.3: + version "7.5.4" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.5.4.tgz#885d31fe9c4b37f25d1bb600da30b1c5b37d286a" + integrity sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg== + dependencies: + "@protobufjs/aspromise" "^1.1.2" + "@protobufjs/base64" "^1.1.2" + "@protobufjs/codegen" "^2.0.4" + "@protobufjs/eventemitter" "^1.1.0" + "@protobufjs/fetch" "^1.1.0" + "@protobufjs/float" "^1.0.2" + "@protobufjs/inquire" "^1.1.0" + "@protobufjs/path" "^1.1.2" + "@protobufjs/pool" "^1.1.0" + "@protobufjs/utf8" "^1.1.0" + "@types/node" ">=13.7.0" + long "^5.0.0" pseudomap@^1.0.2: version "1.0.2" @@ -5817,26 +5172,6 @@ pstree.remy@^1.1.7: resolved "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz" integrity sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w== -public-encrypt@^4.0.0: - version "4.0.3" - resolved "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz" - integrity sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q== - dependencies: - bn.js "^4.1.0" - browserify-rsa "^4.0.0" - create-hash "^1.1.0" - parse-asn1 "^5.0.0" - randombytes "^2.0.1" - safe-buffer "^5.1.2" - -pump@^2.0.0: - version "2.0.1" - resolved "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz" - integrity sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA== - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" - pump@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz" @@ -5845,25 +5180,6 @@ pump@^3.0.0: end-of-stream "^1.1.0" once "^1.3.1" -pumpify@^1.3.3: - version "1.5.1" - resolved "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz" - integrity sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ== - dependencies: - duplexify "^3.6.0" - inherits "^2.0.3" - pump "^2.0.0" - -punycode@1.3.2: - version "1.3.2" - resolved "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz" - integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= - -punycode@^1.2.4: - version "1.4.1" - resolved "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz" - integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= - punycode@^2.1.0: version "2.1.1" resolved "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz" @@ -5881,16 +5197,6 @@ pure-rand@^6.0.0: resolved "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz" integrity sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA== -querystring-es3@^0.2.0: - version "0.2.1" - resolved "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz" - integrity sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM= - -querystring@0.2.0: - version "0.2.0" - resolved "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz" - integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= - querystringify@^2.1.1: version "2.2.0" resolved "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz" @@ -5908,21 +5214,13 @@ random-words@^1.1.1: dependencies: mocha "^7.1.1" -randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0: +randombytes@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz" integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== dependencies: safe-buffer "^5.1.0" -randomfill@^1.0.3: - version "1.0.4" - resolved "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz" - integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw== - dependencies: - randombytes "^2.0.5" - safe-buffer "^5.1.0" - rc@^1.2.8: version "1.2.8" resolved "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz" @@ -5955,10 +5253,20 @@ read-pkg@^2.0.0: normalize-package-data "^2.3.2" path-type "^2.0.0" -"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@^2.3.7, readable-stream@~2.3.6: - version "2.3.7" - resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz" - integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== +"readable-stream@1.x >=1.1.9": + version "1.1.14" + resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz" + integrity sha1-fPTFTvZI44EwhMY23SB54WbAgdk= + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +readable-stream@^2.0.5: + version "2.3.8" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" + integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== dependencies: core-util-is "~1.0.0" inherits "~2.0.3" @@ -5968,15 +5276,18 @@ read-pkg@^2.0.0: string_decoder "~1.1.1" util-deprecate "~1.0.1" -"readable-stream@1.x >=1.1.9": - version "1.1.14" - resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz" - integrity sha1-fPTFTvZI44EwhMY23SB54WbAgdk= +readable-stream@^2.3.5, readable-stream@^2.3.7: + version "2.3.7" + resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz" + integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== dependencies: core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "0.0.1" - string_decoder "~0.10.x" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.5.0: version "3.6.2" @@ -5987,23 +5298,23 @@ readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.5.0: string_decoder "^1.1.1" util-deprecate "^1.0.1" -readable-stream@^3.6.0: - version "3.6.0" - resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz" - integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== +readable-stream@^4.0.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-4.7.0.tgz#cedbd8a1146c13dfff8dab14068028d58c15ac91" + integrity sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg== dependencies: - inherits "^2.0.3" - string_decoder "^1.1.1" - util-deprecate "^1.0.1" + abort-controller "^3.0.0" + buffer "^6.0.3" + events "^3.3.0" + process "^0.11.10" + string_decoder "^1.3.0" -readdirp@^2.2.1: - version "2.2.1" - resolved "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz" - integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ== +readdir-glob@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/readdir-glob/-/readdir-glob-1.1.3.tgz#c3d831f51f5e7bfa62fa2ffbe4b508c640f09584" + integrity sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA== dependencies: - graceful-fs "^4.1.11" - micromatch "^3.1.10" - readable-stream "^2.0.2" + minimatch "^5.1.0" readdirp@~3.2.0: version "3.2.0" @@ -6036,14 +5347,6 @@ regenerator-runtime@^0.13.4: resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz" integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== -regex-not@^1.0.0, regex-not@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz" - integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== - dependencies: - extend-shallow "^3.0.2" - safe-regex "^1.1.0" - regexpp@^3.0.0, regexpp@^3.1.0: version "3.2.0" resolved "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz" @@ -6068,21 +5371,6 @@ registry-url@^5.0.0: dependencies: rc "^1.2.8" -remove-trailing-separator@^1.0.1: - version "1.1.0" - resolved "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz" - integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= - -repeat-element@^1.1.2: - version "1.1.4" - resolved "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz" - integrity sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ== - -repeat-string@^1.6.1: - version "1.6.1" - resolved "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz" - integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= - require-at@^1.0.6: version "1.0.6" resolved "https://registry.npmjs.org/require-at/-/require-at-1.0.6.tgz" @@ -6138,11 +5426,6 @@ resolve-from@^5.0.0: resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== -resolve-url@^0.2.1: - version "0.2.1" - resolved "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz" - integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= - resolve.exports@^2.0.0: version "2.0.3" resolved "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz" @@ -6163,10 +5446,10 @@ responselike@^1.0.2: dependencies: lowercase-keys "^1.0.0" -ret@~0.1.10: - version "0.1.15" - resolved "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz" - integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== +retry@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" + integrity sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow== reusify@^1.0.4: version "1.0.4" @@ -6180,13 +5463,6 @@ rimraf@4.4.1: dependencies: glob "^9.2.0" -rimraf@^2.5.4, rimraf@^2.6.3: - version "2.7.1" - resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz" - integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== - dependencies: - glob "^7.1.3" - rimraf@^3.0.0, rimraf@^3.0.2: version "3.0.2" resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz" @@ -6194,14 +5470,6 @@ rimraf@^3.0.0, rimraf@^3.0.2: dependencies: glob "^7.1.3" -ripemd160@^2.0.0, ripemd160@^2.0.1: - version "2.0.2" - resolved "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz" - integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== - dependencies: - hash-base "^3.0.0" - inherits "^2.0.1" - run-parallel@^1.1.9: version "1.2.0" resolved "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz" @@ -6209,14 +5477,7 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" -run-queue@^1.0.0, run-queue@^1.0.3: - version "1.0.3" - resolved "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz" - integrity sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec= - dependencies: - aproba "^1.1.1" - -safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0, safe-buffer@~5.2.1: +safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0, safe-buffer@~5.2.1: version "5.2.1" resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -6226,19 +5487,12 @@ safe-buffer@~5.1.0, safe-buffer@~5.1.1, safe-buffer@~5.1.2: resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-regex@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz" - integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4= - dependencies: - ret "~0.1.10" - safe-stable-stringify@^1.1.0: version "1.1.1" resolved "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-1.1.1.tgz" integrity sha512-ERq4hUjKDbJfE4+XtZLFPCDi8Vb1JqaxAPTxWFLBx8XcAlf9Bda/ZJdVezs/NAfsMQScyIlUMx+Yeu7P7rx5jw== -safer-buffer@^2.1.0, safer-buffer@~2.1.0: +safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== @@ -6250,14 +5504,15 @@ saslprep@^1.0.0: dependencies: sparse-bitfield "^3.0.3" -schema-utils@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz" - integrity sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g== +schema-utils@^4.3.0, schema-utils@^4.3.3: + version "4.3.3" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.3.3.tgz#5b1850912fa31df90716963d45d9121fdfc09f46" + integrity sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA== dependencies: - ajv "^6.1.0" - ajv-errors "^1.0.0" - ajv-keywords "^3.1.0" + "@types/json-schema" "^7.0.9" + ajv "^8.9.0" + ajv-formats "^2.1.1" + ajv-keywords "^5.1.0" semver-diff@^3.1.1: version "3.1.1" @@ -6266,7 +5521,7 @@ semver-diff@^3.1.1: dependencies: semver "^6.3.0" -"semver@2 || 3 || 4 || 5", semver@^5.1.0, semver@^5.6.0, semver@^5.7.0, semver@^5.7.1: +"semver@2 || 3 || 4 || 5", semver@^5.1.0, semver@^5.7.0, semver@^5.7.1: version "5.7.1" resolved "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -6288,15 +5543,20 @@ semver@^7.2.1, semver@^7.3.4, semver@^7.3.5: dependencies: lru-cache "^6.0.0" -semver@^7.3.2, semver@^7.5.3, semver@^7.5.4, semver@^7.6.3: +semver@^7.3.2, semver@^7.5.3, semver@^7.5.4: version "7.6.3" resolved "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz" integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== -serialize-javascript@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz" - integrity sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw== +semver@^7.7.3: + version "7.7.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.3.tgz#4b5f4143d007633a8dc671cd0a6ef9147b8bb946" + integrity sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q== + +serialize-javascript@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2" + integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g== dependencies: randombytes "^2.1.0" @@ -6305,29 +5565,6 @@ set-blocking@^2.0.0: resolved "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= -set-value@^2.0.0, set-value@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz" - integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== - dependencies: - extend-shallow "^2.0.1" - is-extendable "^0.1.1" - is-plain-object "^2.0.3" - split-string "^3.0.1" - -setimmediate@^1.0.4: - version "1.0.5" - resolved "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz" - integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= - -sha.js@^2.4.0, sha.js@^2.4.8: - version "2.4.11" - resolved "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz" - integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== - dependencies: - inherits "^2.0.1" - safe-buffer "^5.0.1" - shebang-command@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz" @@ -6364,6 +5601,11 @@ signal-exit@^3.0.3, signal-exit@^3.0.7: resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== +signal-exit@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + simple-swizzle@^0.2.2: version "0.2.2" resolved "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz" @@ -6403,57 +5645,11 @@ slice-ansi@^4.0.0: astral-regex "^2.0.0" is-fullwidth-code-point "^3.0.0" -snapdragon-node@^2.0.1: - version "2.1.1" - resolved "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz" - integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== - dependencies: - define-property "^1.0.0" - isobject "^3.0.0" - snapdragon-util "^3.0.1" - -snapdragon-util@^3.0.1: - version "3.0.1" - resolved "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz" - integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== - dependencies: - kind-of "^3.2.0" - -snapdragon@^0.8.1: - version "0.8.2" - resolved "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz" - integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== - dependencies: - base "^0.11.1" - debug "^2.2.0" - define-property "^0.2.5" - extend-shallow "^2.0.1" - map-cache "^0.2.2" - source-map "^0.5.6" - source-map-resolve "^0.5.0" - use "^3.1.0" - -source-list-map@^2.0.0: - version "2.0.1" - resolved "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz" - integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== - source-map-js@^1.2.0: version "1.2.1" resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz" integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== -source-map-resolve@^0.5.0: - version "0.5.3" - resolved "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz" - integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== - dependencies: - atob "^2.1.2" - decode-uri-component "^0.2.0" - resolve-url "^0.2.1" - source-map-url "^0.4.0" - urix "^0.1.0" - source-map-support@0.5.13: version "0.5.13" resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz" @@ -6462,7 +5658,7 @@ source-map-support@0.5.13: buffer-from "^1.0.0" source-map "^0.6.0" -source-map-support@^0.5.17, source-map-support@~0.5.12: +source-map-support@^0.5.17: version "0.5.20" resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.20.tgz" integrity sha512-n1lZZ8Ve4ksRqizaBQgxXDgKwttHDhyfQjA6YZZn8+AroHbsIz+JjwxQDxbp+7y5OYCI8t1Yk7etjD9CRd2hIw== @@ -6470,17 +5666,15 @@ source-map-support@^0.5.17, source-map-support@~0.5.12: buffer-from "^1.0.0" source-map "^0.6.0" -source-map-url@^0.4.0: - version "0.4.1" - resolved "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz" - integrity sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw== - -source-map@^0.5.6: - version "0.5.7" - resolved "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz" - integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= +source-map-support@~0.5.20: + version "0.5.21" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" -source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: +source-map@^0.6.0, source-map@^0.6.1: version "0.6.1" resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== @@ -6523,35 +5717,29 @@ split-ca@^1.0.1: resolved "https://registry.npmjs.org/split-ca/-/split-ca-1.0.1.tgz" integrity sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ== -split-string@^3.0.1, split-string@^3.0.2: - version "3.1.0" - resolved "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz" - integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== - dependencies: - extend-shallow "^3.0.0" - sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz" integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= -ssh2@^1.11.0: - version "1.16.0" - resolved "https://registry.npmjs.org/ssh2/-/ssh2-1.16.0.tgz" - integrity sha512-r1X4KsBGedJqo7h8F5c4Ybpcr5RjyP+aWIG007uBPRjmdQWfEiVLzSK71Zji1B9sKxwaCvD8y8cwSkYrlLiRRg== +ssh-remote-port-forward@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/ssh-remote-port-forward/-/ssh-remote-port-forward-1.0.4.tgz#72b0c5df8ec27ca300c75805cc6b266dee07e298" + integrity sha512-x0LV1eVDwjf1gmG7TTnfqIzf+3VPRz7vrNIjX6oYLbeCrf/PeVY6hkT68Mg+q02qXxQhrLjB0jfgvhevoCRmLQ== + dependencies: + "@types/ssh2" "^0.5.48" + ssh2 "^1.4.0" + +ssh2@^1.15.0, ssh2@^1.4.0: + version "1.17.0" + resolved "https://registry.yarnpkg.com/ssh2/-/ssh2-1.17.0.tgz#dc686e8e3abdbd4ad95d46fa139615903c12258c" + integrity sha512-wPldCk3asibAjQ/kziWQQt1Wh3PgDFpC0XpwclzKcdT1vql6KeYxf5LIt4nlFkUeR8WuphYMKqUA56X4rjbfgQ== dependencies: asn1 "^0.2.6" bcrypt-pbkdf "^1.0.2" optionalDependencies: cpu-features "~0.0.10" - nan "^2.20.0" - -ssri@^6.0.1: - version "6.0.2" - resolved "https://registry.npmjs.org/ssri/-/ssri-6.0.2.tgz" - integrity sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q== - dependencies: - figgy-pudding "^3.5.1" + nan "^2.23.0" stack-trace@0.0.x, stack-trace@^0.0.10: version "0.0.10" @@ -6565,52 +5753,14 @@ stack-utils@^2.0.3: dependencies: escape-string-regexp "^2.0.0" -static-extend@^0.1.1: - version "0.1.2" - resolved "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz" - integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= - dependencies: - define-property "^0.2.5" - object-copy "^0.1.0" - -stream-browserify@^2.0.1: - version "2.0.2" - resolved "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz" - integrity sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg== - dependencies: - inherits "~2.0.1" - readable-stream "^2.0.2" - -stream-each@^1.1.0: - version "1.2.3" - resolved "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz" - integrity sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw== +streamx@^2.15.0, streamx@^2.21.0: + version "2.23.0" + resolved "https://registry.yarnpkg.com/streamx/-/streamx-2.23.0.tgz#7d0f3d00d4a6c5de5728aecd6422b4008d66fd0b" + integrity sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg== dependencies: - end-of-stream "^1.1.0" - stream-shift "^1.0.0" - -stream-http@^2.7.2: - version "2.8.3" - resolved "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz" - integrity sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw== - dependencies: - builtin-status-codes "^3.0.0" - inherits "^2.0.1" - readable-stream "^2.3.6" - to-arraybuffer "^1.0.0" - xtend "^4.0.0" - -stream-shift@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz" - integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== - -stream-to-array@^2.3.0: - version "2.3.0" - resolved "https://registry.npmjs.org/stream-to-array/-/stream-to-array-2.3.0.tgz" - integrity sha512-UsZtOYEn4tWU2RGLOXr/o/xjRBftZRlG3dEWoaHr8j4GuypJ3isitGbVyjQKAuMu+xbiop8q224TjiZWc4XTZA== - dependencies: - any-promise "^1.1.0" + events-universal "^1.0.0" + fast-fifo "^1.3.2" + text-decoder "^1.1.0" string-length@^4.0.1: version "4.0.2" @@ -6620,6 +5770,15 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + "string-width@^1.0.2 || 2": version "2.1.1" resolved "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz" @@ -6646,6 +5805,15 @@ string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2 is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" +string-width@^5.0.1, string-width@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + string.prototype.trimend@^1.0.4: version "1.0.4" resolved "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz" @@ -6662,7 +5830,7 @@ string.prototype.trimstart@^1.0.4: call-bind "^1.0.2" define-properties "^1.1.3" -string_decoder@^1.0.0, string_decoder@^1.1.1: +string_decoder@^1.1.1, string_decoder@^1.3.0: version "1.3.0" resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== @@ -6681,6 +5849,13 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz" @@ -6702,6 +5877,13 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1: dependencies: ansi-regex "^5.0.1" +strip-ansi@^7.0.1: + version "7.1.2" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.2.tgz#132875abde678c7ea8d691533f2e7e22bb744dba" + integrity sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA== + dependencies: + ansi-regex "^6.0.1" + strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz" @@ -6772,32 +5954,33 @@ table@^6.0.9: string-width "^4.2.3" strip-ansi "^6.0.1" -tapable@^1.0.0, tapable@^1.1.3: - version "1.1.3" - resolved "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz" - integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== +tapable@^2.2.0, tapable@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.3.0.tgz#7e3ea6d5ca31ba8e078b560f0d83ce9a14aa8be6" + integrity sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg== -tar-fs@^2.1.0: - version "2.1.1" - resolved "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz" - integrity sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng== +tar-fs@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.4.tgz#800824dbf4ef06ded9afea4acafe71c67c76b930" + integrity sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ== dependencies: chownr "^1.1.1" mkdirp-classic "^0.5.2" pump "^3.0.0" tar-stream "^2.1.4" -tar-fs@~2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/tar-fs/-/tar-fs-2.0.1.tgz" - integrity sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA== +tar-fs@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-3.1.1.tgz#4f164e59fb60f103d472360731e8c6bb4a7fe9ef" + integrity sha512-LZA0oaPOc2fVo82Txf3gw+AkEd38szODlptMYejQUhndHMLQ9M059uXR+AfS7DNo0NpINvSqDsvyaCrBVkptWg== dependencies: - chownr "^1.1.1" - mkdirp-classic "^0.5.2" pump "^3.0.0" - tar-stream "^2.0.0" + tar-stream "^3.1.5" + optionalDependencies: + bare-fs "^4.0.1" + bare-path "^3.0.0" -tar-stream@^2.0.0, tar-stream@^2.1.4: +tar-stream@^2.1.4: version "2.2.0" resolved "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz" integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== @@ -6808,6 +5991,15 @@ tar-stream@^2.0.0, tar-stream@^2.1.4: inherits "^2.0.3" readable-stream "^3.1.1" +tar-stream@^3.0.0, tar-stream@^3.1.5: + version "3.1.7" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-3.1.7.tgz#24b3fb5eabada19fe7338ed6d26e5f7c482e792b" + integrity sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ== + dependencies: + b4a "^1.6.4" + fast-fifo "^1.2.0" + streamx "^2.15.0" + tdigest@^0.1.1: version "0.1.1" resolved "https://registry.npmjs.org/tdigest/-/tdigest-0.1.1.tgz" @@ -6815,29 +6007,26 @@ tdigest@^0.1.1: dependencies: bintrees "1.0.1" -terser-webpack-plugin@^1.4.3: - version "1.4.5" - resolved "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz" - integrity sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw== - dependencies: - cacache "^12.0.2" - find-cache-dir "^2.1.0" - is-wsl "^1.1.0" - schema-utils "^1.0.0" - serialize-javascript "^4.0.0" - source-map "^0.6.1" - terser "^4.1.2" - webpack-sources "^1.4.0" - worker-farm "^1.7.0" - -terser@^4.1.2: - version "4.8.0" - resolved "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz" - integrity sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw== +terser-webpack-plugin@^5.3.16: + version "5.3.16" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.16.tgz#741e448cc3f93d8026ebe4f7ef9e4afacfd56330" + integrity sha512-h9oBFCWrq78NyWWVcSwZarJkZ01c2AyGrzs1crmHZO3QUg9D61Wu4NPjBy69n7JqylFF5y+CsUZYmYEIZ3mR+Q== dependencies: + "@jridgewell/trace-mapping" "^0.3.25" + jest-worker "^27.4.5" + schema-utils "^4.3.0" + serialize-javascript "^6.0.2" + terser "^5.31.1" + +terser@^5.31.1: + version "5.46.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.46.0.tgz#1b81e560d584bbdd74a8ede87b4d9477b0ff9695" + integrity sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg== + dependencies: + "@jridgewell/source-map" "^0.3.3" + acorn "^8.15.0" commander "^2.20.0" - source-map "~0.6.1" - source-map-support "~0.5.12" + source-map-support "~0.5.20" test-exclude@^6.0.0: version "6.0.0" @@ -6848,21 +6037,33 @@ test-exclude@^6.0.0: glob "^7.1.4" minimatch "^3.0.4" -testcontainers@^3.0.0: - version "3.5.0" - resolved "https://registry.npmjs.org/testcontainers/-/testcontainers-3.5.0.tgz" - integrity sha512-iigGuzBDzOmTFlxfaTo1JO7EGMs4Dgf+XHd98XeEwp8J+WzOdkJfsSL5A8fdLSGQA5GvVAdxLcUfT4a32z4rbg== +testcontainers@^11.11.0: + version "11.11.0" + resolved "https://registry.yarnpkg.com/testcontainers/-/testcontainers-11.11.0.tgz#0f397cd3d2a71fc5e1573d36d78149ddb2e0ab3b" + integrity sha512-nKTJn3n/gkyGg/3SVkOwX+isPOGSHlfI+CWMobSmvQrsj7YW01aWvl2pYIfV4LMd+C8or783yYrzKSK2JlP+Qw== dependencies: - "@types/dockerode" "^2.5.34" + "@balena/dockerignore" "^1.0.2" + "@types/dockerode" "^3.3.47" + archiver "^7.0.1" + async-lock "^1.4.1" byline "^5.0.0" - debug "^4.1.1" - docker-compose "^0.23.5" - dockerode "^3.2.1" - get-port "^5.1.1" - glob "^7.1.6" - node-duration "^1.0.4" - stream-to-array "^2.3.0" - tar-fs "^2.1.0" + debug "^4.4.3" + docker-compose "^1.3.0" + dockerode "^4.0.9" + get-port "^7.1.0" + proper-lockfile "^4.1.2" + properties-reader "^2.3.0" + ssh-remote-port-forward "^1.0.4" + tar-fs "^3.1.1" + tmp "^0.2.5" + undici "^7.16.0" + +text-decoder@^1.1.0: + version "1.2.3" + resolved "https://registry.yarnpkg.com/text-decoder/-/text-decoder-1.2.3.tgz#b19da364d981b2326d5f43099c310cc80d770c65" + integrity sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA== + dependencies: + b4a "^1.6.4" text-hex@1.0.x: version "1.0.0" @@ -6874,21 +6075,6 @@ text-table@^0.2.0: resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz" integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= -through2@^2.0.0: - version "2.0.5" - resolved "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz" - integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== - dependencies: - readable-stream "~2.3.6" - xtend "~4.0.1" - -timers-browserify@^2.0.4: - version "2.0.12" - resolved "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz" - integrity sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ== - dependencies: - setimmediate "^1.0.4" - tmp@0.0.x: version "0.0.33" resolved "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz" @@ -6901,36 +6087,21 @@ tmp@^0.2.1: resolved "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz" integrity sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w== +tmp@^0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.5.tgz#b06bcd23f0f3c8357b426891726d16015abfd8f8" + integrity sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow== + tmpl@1.0.5: version "1.0.5" resolved "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz" integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== -to-arraybuffer@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz" - integrity sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M= - -to-object-path@^0.3.0: - version "0.3.0" - resolved "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz" - integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= - dependencies: - kind-of "^3.0.2" - to-readable-stream@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz" integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q== -to-regex-range@^2.1.0: - version "2.1.1" - resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz" - integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= - dependencies: - is-number "^3.0.0" - repeat-string "^1.6.1" - to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz" @@ -6938,16 +6109,6 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -to-regex@^3.0.1, to-regex@^3.0.2: - version "3.0.2" - resolved "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz" - integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== - dependencies: - define-property "^2.0.2" - extend-shallow "^3.0.2" - regex-not "^1.0.2" - safe-regex "^1.1.0" - toposort@^2.0.2: version "2.0.2" resolved "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz" @@ -6965,19 +6126,19 @@ triple-beam@^1.2.0, triple-beam@^1.3.0: resolved "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz" integrity sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw== -ts-jest@^29.0.3: - version "29.2.5" - resolved "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.5.tgz" - integrity sha512-KD8zB2aAZrcKIdGk4OwpJggeLcH1FgrICqDSROWqlnJXGCXK4Mn6FcdK2B6670Xr73lHMG1kHw8R87A0ecZ+vA== +ts-jest@^29.2.0: + version "29.4.6" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.4.6.tgz#51cb7c133f227396818b71297ad7409bb77106e9" + integrity sha512-fSpWtOO/1AjSNQguk43hb/JCo16oJDnMJf3CdEGNkqsEX3t0KX96xvyX1D7PfLCpVoKu4MfVrqUkFyblYoY4lA== dependencies: bs-logger "^0.2.6" - ejs "^3.1.10" fast-json-stable-stringify "^2.1.0" - jest-util "^29.0.0" + handlebars "^4.7.8" json5 "^2.2.3" lodash.memoize "^4.1.2" make-error "^1.3.6" - semver "^7.6.3" + semver "^7.7.3" + type-fest "^4.41.0" yargs-parser "^21.1.1" ts-node@^8.10.1, ts-node@^8.3.0, ts-node@^8.8.2: @@ -7013,11 +6174,6 @@ tsutils@^3.21.0: dependencies: tslib "^1.8.1" -tty-browserify@0.0.0: - version "0.0.0" - resolved "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz" - integrity sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY= - tweetnacl@^0.14.3: version "0.14.5" resolved "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz" @@ -7055,6 +6211,11 @@ type-fest@^0.21.3: resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== +type-fest@^4.41.0: + version "4.41.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-4.41.0.tgz#6ae1c8e5731273c2bf1f58ad39cbae2c91a46c58" + integrity sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA== + typedarray-to-buffer@^3.1.5: version "3.1.5" resolved "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz" @@ -7062,11 +6223,6 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" -typedarray@^0.0.6: - version "0.0.6" - resolved "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz" - integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= - typescript@^3.5.3, typescript@^3.7.5: version "3.9.10" resolved "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz" @@ -7077,6 +6233,11 @@ typescript@^4.9.4: resolved "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz" integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== +uglify-js@^3.1.4: + version "3.19.3" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.19.3.tgz#82315e9bbc6f2b25888858acd1fff8441035b77f" + integrity sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ== + unbox-primitive@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz" @@ -7099,29 +6260,20 @@ undici-types@~5.26.4: resolved "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz" integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== -union-value@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz" - integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== - dependencies: - arr-union "^3.1.0" - get-value "^2.0.6" - is-extendable "^0.1.1" - set-value "^2.0.1" +undici-types@~6.21.0: + version "6.21.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.21.0.tgz#691d00af3909be93a7faa13be61b3a5b50ef12cb" + integrity sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ== -unique-filename@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz" - integrity sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ== - dependencies: - unique-slug "^2.0.0" +undici-types@~7.16.0: + version "7.16.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.16.0.tgz#ffccdff36aea4884cbfce9a750a0580224f58a46" + integrity sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw== -unique-slug@^2.0.0: - version "2.0.2" - resolved "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz" - integrity sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w== - dependencies: - imurmurhash "^0.1.4" +undici@^7.16.0: + version "7.19.1" + resolved "https://registry.yarnpkg.com/undici/-/undici-7.19.1.tgz#b8a35742564a27601efa893a8800ce60d5502019" + integrity sha512-Gpq0iNm5M6cQWlyHQv9MV+uOj1jWk7LpkoE5vSp/7zjb4zMdAcUD+VL5y0nH4p9EbUklq00eVIIX/XcDHzu5xg== unique-string@^2.0.0: version "2.0.0" @@ -7140,19 +6292,6 @@ universalify@^2.0.0: resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz" integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== -unset-value@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz" - integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= - dependencies: - has-value "^0.3.1" - isobject "^3.0.0" - -upath@^1.1.1: - version "1.2.0" - resolved "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz" - integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== - update-browserslist-db@^1.1.1: version "1.1.2" resolved "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz" @@ -7161,6 +6300,14 @@ update-browserslist-db@^1.1.1: escalade "^3.2.0" picocolors "^1.1.1" +update-browserslist-db@^1.2.0: + version "1.2.3" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz#64d76db58713136acbeb4c49114366cc6cc2e80d" + integrity sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w== + dependencies: + escalade "^3.2.0" + picocolors "^1.1.1" + update-notifier@^5.1.0: version "5.1.0" resolved "https://registry.npmjs.org/update-notifier/-/update-notifier-5.1.0.tgz" @@ -7188,11 +6335,6 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" -urix@^0.1.0: - version "0.1.0" - resolved "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz" - integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= - url-parse-lax@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz" @@ -7208,19 +6350,6 @@ url-parse@~1.5.1: querystringify "^2.1.1" requires-port "^1.0.0" -url@^0.11.0: - version "0.11.0" - resolved "https://registry.npmjs.org/url/-/url-0.11.0.tgz" - integrity sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE= - dependencies: - punycode "1.3.2" - querystring "0.2.0" - -use@^3.1.0: - version "3.1.1" - resolved "https://registry.npmjs.org/use/-/use-3.1.1.tgz" - integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== - useragent@^2.3.0: version "2.3.0" resolved "https://registry.npmjs.org/useragent/-/useragent-2.3.0.tgz" @@ -7234,25 +6363,16 @@ util-deprecate@^1.0.1, util-deprecate@~1.0.1: resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= -util@0.10.3: - version "0.10.3" - resolved "https://registry.npmjs.org/util/-/util-0.10.3.tgz" - integrity sha1-evsa/lCAUkZInj23/g7TeTNqwPk= - dependencies: - inherits "2.0.1" - -util@^0.11.0: - version "0.11.1" - resolved "https://registry.npmjs.org/util/-/util-0.11.1.tgz" - integrity sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ== - dependencies: - inherits "2.0.3" - uuid@8.3.2, uuid@^8.3.0: version "8.3.2" resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== +uuid@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-10.0.0.tgz#5a95aa454e6e002725c79055fd42aaba30ca6294" + integrity sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ== + v8-compile-cache@^2.0.3: version "2.3.0" resolved "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz" @@ -7275,11 +6395,6 @@ validate-npm-package-license@^3.0.1: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" -vm-browserify@^1.0.1: - version "1.1.2" - resolved "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz" - integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== - wait-for-expect@^3.0.2: version "3.0.2" resolved "https://registry.npmjs.org/wait-for-expect/-/wait-for-expect-3.0.2.tgz" @@ -7299,60 +6414,49 @@ walker@^1.0.8: dependencies: makeerror "1.0.12" -watchpack-chokidar2@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz" - integrity sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww== - dependencies: - chokidar "^2.1.8" - -watchpack@^1.7.4: - version "1.7.5" - resolved "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz" - integrity sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ== +watchpack@^2.4.4: + version "2.5.1" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.5.1.tgz#dd38b601f669e0cbf567cb802e75cead82cde102" + integrity sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg== dependencies: + glob-to-regexp "^0.4.1" graceful-fs "^4.1.2" - neo-async "^2.5.0" - optionalDependencies: - chokidar "^3.4.1" - watchpack-chokidar2 "^2.0.1" - -webpack-sources@^1.4.0, webpack-sources@^1.4.1: - version "1.4.3" - resolved "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz" - integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ== - dependencies: - source-list-map "^2.0.0" - source-map "~0.6.1" - -webpack@^4.39.3, webpack@^4.43.0: - version "4.46.0" - resolved "https://registry.npmjs.org/webpack/-/webpack-4.46.0.tgz" - integrity sha512-6jJuJjg8znb/xRItk7bkT0+Q7AHCYjjFnvKIWQPkNIOyRqoCGvkOs0ipeQzrqz4l5FtN5ZI/ukEHroeX/o1/5Q== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-module-context" "1.9.0" - "@webassemblyjs/wasm-edit" "1.9.0" - "@webassemblyjs/wasm-parser" "1.9.0" - acorn "^6.4.1" - ajv "^6.10.2" - ajv-keywords "^3.4.1" + +webpack-sources@^3.3.3: + version "3.3.3" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.3.3.tgz#d4bf7f9909675d7a070ff14d0ef2a4f3c982c723" + integrity sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg== + +webpack@^5.95.0: + version "5.104.1" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.104.1.tgz#94bd41eb5dbf06e93be165ba8be41b8260d4fb1a" + integrity sha512-Qphch25abbMNtekmEGJmeRUhLDbe+QfiWTiqpKYkpCOWY64v9eyl+KRRLmqOFA2AvKPpc9DC6+u2n76tQLBoaA== + dependencies: + "@types/eslint-scope" "^3.7.7" + "@types/estree" "^1.0.8" + "@types/json-schema" "^7.0.15" + "@webassemblyjs/ast" "^1.14.1" + "@webassemblyjs/wasm-edit" "^1.14.1" + "@webassemblyjs/wasm-parser" "^1.14.1" + acorn "^8.15.0" + acorn-import-phases "^1.0.3" + browserslist "^4.28.1" chrome-trace-event "^1.0.2" - enhanced-resolve "^4.5.0" - eslint-scope "^4.0.3" - json-parse-better-errors "^1.0.2" - loader-runner "^2.4.0" - loader-utils "^1.2.3" - memory-fs "^0.4.1" - micromatch "^3.1.10" - mkdirp "^0.5.3" - neo-async "^2.6.1" - node-libs-browser "^2.2.1" - schema-utils "^1.0.0" - tapable "^1.1.3" - terser-webpack-plugin "^1.4.3" - watchpack "^1.7.4" - webpack-sources "^1.4.1" + enhanced-resolve "^5.17.4" + es-module-lexer "^2.0.0" + eslint-scope "5.1.1" + events "^3.2.0" + glob-to-regexp "^0.4.1" + graceful-fs "^4.2.11" + json-parse-even-better-errors "^2.3.1" + loader-runner "^4.3.1" + mime-types "^2.1.27" + neo-async "^2.6.2" + schema-utils "^4.3.3" + tapable "^2.3.0" + terser-webpack-plugin "^5.3.16" + watchpack "^2.4.4" + webpack-sources "^3.3.3" which-boxed-primitive@^1.0.2: version "1.0.2" @@ -7426,12 +6530,19 @@ word-wrap@^1.2.3: resolved "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== -worker-farm@^1.7.0: - version "1.7.0" - resolved "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz" - integrity sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw== +wordwrap@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== + +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== dependencies: - errno "~0.1.7" + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" wrap-ansi@^5.1.0: version "5.1.0" @@ -7460,6 +6571,15 @@ wrap-ansi@^7.0.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" + wrappy@1: version "1.0.2" resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" @@ -7488,11 +6608,6 @@ xdg-basedir@^4.0.0: resolved "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz" integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q== -xtend@^4.0.0, xtend@~4.0.1: - version "4.0.2" - resolved "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz" - integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== - y18n@^4.0.0: version "4.0.3" resolved "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz" @@ -7518,10 +6633,10 @@ yallist@^3.0.2: resolved "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== -yaml@^1.10.2: - version "1.10.2" - resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz" - integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== +yaml@^2.2.2: + version "2.8.2" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.8.2.tgz#5694f25eca0ce9c3e7a9d9e00ce0ddabbd9e35c5" + integrity sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A== yargs-parser@13.1.2, yargs-parser@^13.1.2: version "13.1.2" @@ -7586,7 +6701,7 @@ yargs@^15.3.1: y18n "^4.0.0" yargs-parser "^18.1.2" -yargs@^17.3.1: +yargs@^17.3.1, yargs@^17.7.2: version "17.7.2" resolved "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz" integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== @@ -7629,3 +6744,12 @@ yup@^0.28.5: property-expr "^2.0.2" synchronous-promise "^2.0.10" toposort "^2.0.2" + +zip-stream@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-6.0.1.tgz#e141b930ed60ccaf5d7fa9c8260e0d1748a2bbfb" + integrity sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA== + dependencies: + archiver-utils "^5.0.0" + compress-commons "^6.0.2" + readable-stream "^4.0.0" From 2516e1ab1b53f4c244dd64cea8f60663f2f47078 Mon Sep 17 00:00:00 2001 From: Peter Date: Wed, 28 Jan 2026 13:35:16 +0300 Subject: [PATCH 07/20] Task manager (#511) * Add Task Manager worker for auto GitHub issue creation Introduces a new worker at workers/task-manager that automatically creates GitHub issues for events meeting a threshold, with daily rate limiting and atomic usage tracking. Includes environment setup, documentation, and updates @hawk.so/types to v0.5.3. * Integrate GitHub issue creation and Copilot assignment Added GitHubService for authenticating as a GitHub App and creating issues via the GitHub API. Implemented formatting of issue data from events, including stacktrace and source code snippets. Updated TaskManagerWorker to use real GitHub issue creation and Copilot assignment, replacing previous mocked logic. Added environment variables for GitHub App configuration and updated documentation. Included tests for issue formatting. * Refactor GitHub key handling and improve Copilot assignment Extracted GitHub App private key normalization to a utility for better reliability and CI compatibility. Enhanced Copilot assignment to use the GraphQL API and improved error handling. Refactored task creation flow to increment usage only after successful issue creation, updated dependencies, and fixed import paths. * use pat * Add delegated user OAuth support and token refresh for GitHub integration Introduces delegated user-to-server OAuth support for GitHub App integration in the task manager worker. Adds logic for handling delegated user tokens, including automatic refresh and fallback to installation tokens, and updates environment/configuration to support GitHub App OAuth credentials. Updates dependencies to include @octokit/oauth-methods and related packages. * Refactor GitHub issue creation and Copilot assignment Separated GitHub issue creation and Copilot agent assignment into distinct steps. The issue is now always created using the GitHub App installation token, and Copilot is assigned afterward using a user-to-server OAuth token if enabled. Updated the TaskManagerWorker logic to reflect this change, improved error handling, and updated the event saving logic to accurately reflect Copilot assignment status. * Update GithubService.ts * lint code * lint and tests * Fix task manager env parsing and event timestamp filter Replaces Number() with parseInt() for MAX_AUTO_TASKS_PER_DAY to ensure correct parsing. Fixes event query to filter by timestamp using connectedAt, and enables the super.start() call. Also corrects a typo in a comment in GrouperWorker. * Update package.json * update issue format * lint --- package.json | 8 +- workers/grouper/src/index.ts | 10 +- workers/task-manager/.env.example | 15 + workers/task-manager/README.md | 32 + workers/task-manager/package.json | 16 + workers/task-manager/src/GithubService.ts | 633 ++++++++++++++++ workers/task-manager/src/env.ts | 7 + workers/task-manager/src/index.ts | 690 ++++++++++++++++++ .../src/utils/githubPrivateKey.ts | 58 ++ workers/task-manager/src/utils/issue.ts | 260 +++++++ .../types/task-manager-worker-task.ts | 11 + yarn.lock | 258 ++++++- 12 files changed, 1988 insertions(+), 10 deletions(-) create mode 100644 workers/task-manager/.env.example create mode 100644 workers/task-manager/README.md create mode 100644 workers/task-manager/package.json create mode 100644 workers/task-manager/src/GithubService.ts create mode 100644 workers/task-manager/src/env.ts create mode 100644 workers/task-manager/src/index.ts create mode 100644 workers/task-manager/src/utils/githubPrivateKey.ts create mode 100644 workers/task-manager/src/utils/issue.ts create mode 100644 workers/task-manager/types/task-manager-worker-task.ts diff --git a/package.json b/package.json index 1e8e621a9..5b69d4e86 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "hawk.workers", "private": true, - "version": "0.0.1", + "version": "0.1.0", "description": "Hawk workers", "repository": "git@github.com:codex-team/hawk.workers.git", "license": "BUSL-1.1", @@ -33,6 +33,7 @@ "test:paymaster": "jest workers/paymaster", "test:notifier": "jest workers/notifier", "test:js": "jest workers/javascript", + "test:task-manager": "jest workers/task-manager", "test:clear": "jest --clearCache", "run-default": "yarn worker hawk-worker-default", "run-sentry": "yarn worker hawk-worker-sentry", @@ -47,7 +48,8 @@ "run-release": "yarn worker hawk-worker-release", "run-email": "yarn worker hawk-worker-email", "run-telegram": "yarn worker hawk-worker-telegram", - "run-limiter": "yarn worker hawk-worker-limiter" + "run-limiter": "yarn worker hawk-worker-limiter", + "run-task-manager": "yarn worker hawk-worker-task-manager" }, "dependencies": { "@babel/parser": "^7.26.9", @@ -61,7 +63,7 @@ "amqplib": "^0.8.0", "codex-accounting-sdk": "codex-team/codex-accounting-sdk", "debug": "^4.1.1", - "dotenv": "^8.2.0", + "dotenv": "^17.2.3", "migrate-mongo": "^7.2.1", "mockdate": "^3.0.2", "mongodb": "^3.5.7", diff --git a/workers/grouper/src/index.ts b/workers/grouper/src/index.ts index 8d20daf56..73f16fc77 100644 --- a/workers/grouper/src/index.ts +++ b/workers/grouper/src/index.ts @@ -132,12 +132,10 @@ export default class GrouperWorker extends Worker { uniqueEventHash = similarEvent.groupHash; existedEvent = similarEvent; - } - - /** - * If we couldn't group by grouping pattern — try grouping bt hash (title) - */ - else { + } else { + /** + * If we couldn't group by grouping pattern — try grouping by hash (title) + */ /** * Find event by group hash. */ diff --git a/workers/task-manager/.env.example b/workers/task-manager/.env.example new file mode 100644 index 000000000..c62cf58c2 --- /dev/null +++ b/workers/task-manager/.env.example @@ -0,0 +1,15 @@ +# Maximum number of auto-created tasks per project per day +# Default: 10 +MAX_AUTO_TASKS_PER_DAY=10 + +# Number of tasks handling simultaneously +# Default: 1 +SIMULTANEOUS_TASKS=1 + +# GitHub App configuration +GITHUB_APP_ID=your_github_app_id +GITHUB_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----" +# Client ID of GitHub app +GITHUB_APP_CLIENT_ID=Iv23li65HEIkWZXsm6qO +# Generated in GitHub app settings +GITHUB_APP_CLIENT_SECRET=0663e20d484234e17b0871c1f070581739c14e04 diff --git a/workers/task-manager/README.md b/workers/task-manager/README.md new file mode 100644 index 000000000..236e61010 --- /dev/null +++ b/workers/task-manager/README.md @@ -0,0 +1,32 @@ +# Task Manager Worker + +Worker for automatically creating GitHub issues for errors that meet the threshold. + +## Description + +This worker processes tasks to automatically create GitHub issues for events that: +- Have `totalCount >= taskThresholdTotalCount` +- Don't have a `taskManagerItem` (not yet processed) +- Occurred after `taskManager.connectedAt` + +## Rate Limiting + +The worker implements daily rate limiting: +- Maximum `MAX_AUTO_TASKS_PER_DAY` (default: 10) tasks per project per day +- Uses atomic increment to prevent race conditions +- Resets daily budget at the start of each UTC day + +## Environment Variables + +- `MAX_AUTO_TASKS_PER_DAY` - Maximum auto tasks per day (default: 10) +- `GITHUB_APP_ID` - GitHub App ID +- `GITHUB_PRIVATE_KEY` - GitHub App private key (PEM format) + +## Usage + +The worker is triggered by cron tasks with routing key `cron-tasks/task-manager` and payload: +```json +{ + "type": "auto-task-creation" +} +``` diff --git a/workers/task-manager/package.json b/workers/task-manager/package.json new file mode 100644 index 000000000..e28d16a94 --- /dev/null +++ b/workers/task-manager/package.json @@ -0,0 +1,16 @@ +{ + "name": "hawk-worker-task-manager", + "version": "1.0.0", + "main": "src/index.ts", + "license": "MIT", + "workerType": "cron-tasks/task-manager", + "dependencies": { + "@octokit/oauth-methods": "^4.0.0", + "@octokit/rest": "^22.0.1", + "@octokit/types": "^16.0.0", + "jsonwebtoken": "^9.0.3" + }, + "devDependencies": { + "@types/jsonwebtoken": "^8.3.5" + } +} diff --git a/workers/task-manager/src/GithubService.ts b/workers/task-manager/src/GithubService.ts new file mode 100644 index 000000000..476a88c27 --- /dev/null +++ b/workers/task-manager/src/GithubService.ts @@ -0,0 +1,633 @@ +import jwt from 'jsonwebtoken'; +import { Octokit } from '@octokit/rest'; +import type { Endpoints } from '@octokit/types'; +import TimeMs from '../../../lib/utils/time'; +import { normalizeGitHubPrivateKey } from './utils/githubPrivateKey'; +import { refreshToken as refreshOAuthToken } from '@octokit/oauth-methods'; + +/** + * Type for GitHub Issue creation parameters + */ +export type IssueData = Pick< + Endpoints['POST /repos/{owner}/{repo}/issues']['parameters'], + 'title' | 'body' | 'labels' +>; + +/** + * Type for GitHub Issue response data + */ +export type GitHubIssue = Pick< + Endpoints['POST /repos/{owner}/{repo}/issues']['response']['data'], + 'number' | 'html_url' | 'title' | 'state' +>; + +/** + * Service for interacting with GitHub API from workers + * Simplified version of api/src/integrations/github/service.ts + * Only includes methods needed for creating issues + */ +export class GitHubService { + /** + * Default timeout for GitHub API requests (in milliseconds) + */ + private static readonly DEFAULT_TIMEOUT = 10000; + + /** + * Minutes in 10 minutes (JWT expiration time) + */ + private static readonly JWT_EXPIRATION_MINUTES = 10; + + /** + * Minutes for token refresh buffer + */ + private static readonly TOKEN_REFRESH_BUFFER_MINUTES = 5; + + /** + * Number of assignees to fetch in GraphQL query + */ + private static readonly ASSIGNEES_QUERY_LIMIT = 20; + + /** + * GitHub App ID from environment variables + */ + private readonly appId?: string; + + /** + * GitHub App Client ID from environment variables (optional, needed for token refresh) + */ + private readonly clientId?: string; + + /** + * GitHub App Client Secret from environment variables (optional, needed for token refresh) + */ + private readonly clientSecret?: string; + + /** + * Creates an instance of GitHubService + * Requires GitHub App authentication + */ + constructor() { + if (!process.env.GITHUB_APP_ID) { + throw new Error('GITHUB_APP_ID environment variable must be set'); + } + + this.appId = process.env.GITHUB_APP_ID; + + /** + * Client ID and Secret are optional but needed for token refresh + */ + if (process.env.GITHUB_APP_CLIENT_ID) { + this.clientId = process.env.GITHUB_APP_CLIENT_ID; + } + + if (process.env.GITHUB_APP_CLIENT_SECRET) { + this.clientSecret = process.env.GITHUB_APP_CLIENT_SECRET; + } + } + + /** + * Create a GitHub issue using GitHub App installation token + * + * @param {string} repoFullName - Repository full name (owner/repo) + * @param {string | null} installationId - GitHub App installation ID + * @param {IssueData} issueData - Issue data (title, body, labels) + * @returns {Promise} Created issue + * @throws {Error} If issue creation fails + */ + public async createIssue( + repoFullName: string, + installationId: string | null, + issueData: IssueData + ): Promise { + const [owner, repo] = repoFullName.split('/'); + + if (!owner || !repo) { + throw new Error(`Invalid repository name format: ${repoFullName}. Expected format: owner/repo`); + } + + /** + * Get installation access token (GitHub App token) + */ + const accessToken = await this.getAuthToken(installationId); + + /** + * Create Octokit instance with installation token and configured timeout + */ + const octokit = this.createOctokit(accessToken); + + /** + * Create issue via REST API using installation token + */ + return this.createIssueViaRest(octokit, owner, repo, issueData); + } + + /** + * Assign Copilot agent to a GitHub issue using user-to-server OAuth token + * + * @param {string} repoFullName - Repository full name (owner/repo) + * @param {number} issueNumber - Issue number + * @param {string} delegatedUserToken - User-to-server OAuth token + * @returns {Promise} + * @throws {Error} If Copilot assignment fails + */ + public async assignCopilot( + repoFullName: string, + issueNumber: number, + delegatedUserToken: string + ): Promise { + const [owner, repo] = repoFullName.split('/'); + + if (!owner || !repo) { + throw new Error(`Invalid repository name format: ${repoFullName}. Expected format: owner/repo`); + } + + /** + * Create Octokit instance with user-to-server OAuth token + */ + const octokit = this.createOctokit(delegatedUserToken); + + try { + /** + * Step 1: Get repository ID and find Copilot bot ID + */ + const suggestedActorsLimit = GitHubService.ASSIGNEES_QUERY_LIMIT; + const repoInfoQuery = ` + query($owner: String!, $name: String!, $issueNumber: Int!) { + repository(owner: $owner, name: $name) { + id + issue(number: $issueNumber) { + id + } + suggestedActors(capabilities: [CAN_BE_ASSIGNED], first: ${suggestedActorsLimit}) { + nodes { + login + __typename + ... on Bot { + id + } + ... on User { + id + } + } + } + } + } + `; + + const repoInfo: any = await octokit.graphql(repoInfoQuery, { + owner, + name: repo, + issueNumber, + }); + + const JSON_INDENT_SPACES = 2; + + console.log('[GitHub API] Repository info query response:', JSON.stringify(repoInfo, null, JSON_INDENT_SPACES)); + + const repositoryId = repoInfo?.repository?.id; + const issueId = repoInfo?.repository?.issue?.id; + + if (!repositoryId) { + throw new Error(`Failed to get repository ID for ${repoFullName}`); + } + + if (!issueId) { + throw new Error(`Failed to get issue ID for issue #${issueNumber}`); + } + + /** + * Find Copilot bot in suggested actors + */ + let copilotBot = repoInfo.repository.suggestedActors.nodes.find( + (node: any) => node.login === 'copilot-swe-agent' + ); + + console.log('[GitHub API] Copilot bot found in suggestedActors:', copilotBot + ? { + login: copilotBot.login, + id: copilotBot.id, + } + : 'not found'); + + /** + * If not found in suggestedActors, try to get it directly by login + */ + if (!copilotBot || !copilotBot.id) { + console.log('[GitHub API] Trying to get Copilot bot directly by login...'); + + try { + const copilotBotQuery = ` + query($login: String!) { + user(login: $login) { + id + login + __typename + } + } + `; + + const copilotUserInfo: any = await octokit.graphql(copilotBotQuery, { + login: 'copilot-swe-agent', + }); + + console.log('[GitHub API] Direct Copilot bot query response:', JSON.stringify(copilotUserInfo, null, JSON_INDENT_SPACES)); + + if (copilotUserInfo?.user?.id) { + copilotBot = { + login: copilotUserInfo.user.login, + id: copilotUserInfo.user.id, + }; + } + } catch (directQueryError) { + console.log('[GitHub API] Failed to get Copilot bot directly:', directQueryError); + } + } + + if (!copilotBot || !copilotBot.id) { + throw new Error('Copilot coding agent (copilot-swe-agent) is not available for this repository'); + } + + console.log('[GitHub API] Using Copilot bot:', { + login: copilotBot.login, + id: copilotBot.id, + }); + + /** + * Step 2: Assign Copilot to issue via GraphQL + * Note: Assignable is a union type (Issue | PullRequest), so we need to use fragments + */ + const assignCopilotMutation = ` + mutation($issueId: ID!, $assigneeIds: [ID!]!) { + addAssigneesToAssignable(input: { + assignableId: $issueId + assigneeIds: $assigneeIds + }) { + assignable { + ... on Issue { + id + number + assignees(first: 10) { + nodes { + login + } + } + } + ... on PullRequest { + id + number + assignees(first: 10) { + nodes { + login + } + } + } + } + } + } + `; + + const response: any = await octokit.graphql(assignCopilotMutation, { + issueId, + assigneeIds: [ copilotBot.id ], + }); + + console.log('[GitHub API] Assign Copilot mutation response:', JSON.stringify(response, null, JSON_INDENT_SPACES)); + + const assignable = response?.addAssigneesToAssignable?.assignable; + + if (!assignable) { + throw new Error('Failed to assign Copilot to issue'); + } + + /** + * Assignable is a union type (Issue | PullRequest), so we need to check which type it is + * Both Issue and PullRequest have assignees field, so we can access it directly + * + * Note: The assignees list might not be immediately updated in the response, + * so we check if the mutation succeeded (assignable is not null) rather than + * verifying the assignees list directly + */ + const assignedLogins = assignable.assignees?.nodes?.map((n: any) => n.login) || []; + + /** + * Log assignees for debugging (but don't fail if Copilot is not in the list yet) + * GitHub API might not immediately reflect the assignment in the response + */ + console.log(`[GitHub API] Issue assignees after mutation:`, assignedLogins); + + /** + * Get issue number from assignable (works for both Issue and PullRequest) + */ + const assignedNumber = assignable.number; + + /** + * If Copilot is in the list, log success. Otherwise, just log a warning + * but don't throw an error, as the mutation might have succeeded even if + * the response doesn't show the assignee yet + */ + if (assignedLogins.includes('copilot-swe-agent')) { + console.log(`[GitHub API] Successfully assigned Copilot to issue #${assignedNumber}`); + } else { + /** + * Mutation succeeded (assignable is not null), but assignees list might not be updated yet + * This is a known behavior of GitHub API - the mutation succeeds but the response + * might not immediately reflect the new assignee + */ + console.log(`[GitHub API] Copilot assignment mutation completed for issue #${assignedNumber}, but assignees list not yet updated in response`); + } + } catch (error) { + throw new Error(`Failed to assign Copilot: ${error instanceof Error ? error.message : String(error)}`); + } + } + + /** + * Get valid access token with automatic refresh if needed + * Checks if token is expired or close to expiration and refreshes if necessary + * + * @param {Object} tokenInfo - Current token information + * @param {string} tokenInfo.accessToken - Current access token + * @param {string} tokenInfo.refreshToken - Refresh token + * @param {Date | null} tokenInfo.accessTokenExpiresAt - Access token expiration date + * @param {Date | null} tokenInfo.refreshTokenExpiresAt - Refresh token expiration date + * @param {Function} onRefresh - Callback to save refreshed tokens (called after successful refresh) + * @returns {Promise} Valid access token + * @throws {Error} If token refresh fails or refresh token is expired + */ + public async getValidAccessToken( + tokenInfo: { + accessToken: string; + refreshToken: string; + accessTokenExpiresAt: Date | null; + refreshTokenExpiresAt: Date | null; + }, + onRefresh?: (newTokens: { + accessToken: string; + refreshToken: string; + expiresAt: Date | null; + refreshTokenExpiresAt: Date | null; + }) => Promise + ): Promise { + const now = new Date(); + const bufferTime = GitHubService.TOKEN_REFRESH_BUFFER_MINUTES * TimeMs.MINUTE; // 5 minutes buffer before expiration + + /** + * Check if access token is expired or close to expiration + */ + if (tokenInfo.accessTokenExpiresAt) { + const timeUntilExpiration = tokenInfo.accessTokenExpiresAt.getTime() - now.getTime(); + + if (timeUntilExpiration <= bufferTime) { + /** + * Token is expired or close to expiration, need to refresh + */ + if (!tokenInfo.refreshToken) { + throw new Error('Access token expired and no refresh token available'); + } + + /** + * Check if refresh token is expired + */ + if (tokenInfo.refreshTokenExpiresAt && tokenInfo.refreshTokenExpiresAt <= now) { + throw new Error('Refresh token is expired'); + } + + if (!this.clientId || !this.clientSecret) { + throw new Error('GITHUB_APP_CLIENT_ID and GITHUB_APP_CLIENT_SECRET are required for token refresh'); + } + + /** + * Refresh the token + */ + const newTokens = await this.refreshUserToken(tokenInfo.refreshToken); + + /** + * Save refreshed tokens if callback provided + */ + if (onRefresh) { + await onRefresh(newTokens); + } + + return newTokens.accessToken; + } + } + + /** + * Token is still valid, return it + */ + return tokenInfo.accessToken; + } + + /** + * Refresh user-to-server access token using refresh token + * Rotates refresh token if a new one is provided + * + * @param {string} refreshToken - OAuth refresh token + * @returns {Promise<{ accessToken: string; refreshToken: string; expiresAt: Date | null; refreshTokenExpiresAt: Date | null }>} New tokens + * @throws {Error} If token refresh fails + */ + public async refreshUserToken(refreshToken: string): Promise<{ + accessToken: string; + refreshToken: string; + expiresAt: Date | null; + refreshTokenExpiresAt: Date | null; + }> { + if (!this.clientId || !this.clientSecret) { + throw new Error('GITHUB_APP_CLIENT_ID and GITHUB_APP_CLIENT_SECRET are required for token refresh'); + } + + try { + const { authentication } = await refreshOAuthToken({ + clientType: 'github-app', + clientId: this.clientId, + clientSecret: this.clientSecret, + refreshToken, + }); + + if (!authentication.token) { + throw new Error('No access token in refresh response'); + } + + /** + * refreshToken, expiresAt, and refreshTokenExpiresAt are only available in certain authentication types + * Use type guards to safely access these properties + */ + const newRefreshToken = 'refreshToken' in authentication && authentication.refreshToken + ? authentication.refreshToken + : refreshToken; // Use new refresh token if provided, otherwise keep old one + + return { + accessToken: authentication.token, + refreshToken: newRefreshToken, + expiresAt: 'expiresAt' in authentication && authentication.expiresAt + ? new Date(authentication.expiresAt) + : null, + refreshTokenExpiresAt: 'refreshTokenExpiresAt' in authentication && authentication.refreshTokenExpiresAt + ? new Date(authentication.refreshTokenExpiresAt) + : null, + }; + } catch (error) { + throw new Error(`Failed to refresh user token: ${error instanceof Error ? error.message : String(error)}`); + } + } + + /** + * Create a GitHub issue via REST API (helper method) + * + * @param {Octokit} octokit - Octokit instance + * @param {string} owner - Repository owner + * @param {string} repo - Repository name + * @param {IssueData} issueData - Issue data (title, body, labels) + * @returns {Promise} Created issue + * @throws {Error} If issue creation fails + */ + private async createIssueViaRest( + octokit: Octokit, + owner: string, + repo: string, + issueData: IssueData + ): Promise { + try { + const { data } = await octokit.rest.issues.create({ + owner, + repo, + title: issueData.title, + body: issueData.body, + labels: issueData.labels, + }); + + // eslint-disable-next-line @typescript-eslint/naming-convention + const { html_url } = data; + + const JSON_INDENT_SPACES = 2; + + console.log('[GitHub API] Create issue response:', JSON.stringify({ + number: data.number, + // eslint-disable-next-line @typescript-eslint/naming-convention + html_url, + title: data.title, + state: data.state, + assignees: data.assignees?.map(a => a.login) || [], + }, null, JSON_INDENT_SPACES)); + + return { + number: data.number, + // eslint-disable-next-line @typescript-eslint/naming-convention + html_url, + title: data.title, + state: data.state, + }; + } catch (error) { + throw new Error(`Failed to create issue: ${error instanceof Error ? error.message : String(error)}`); + } + } + + /** + * Create Octokit instance with configured timeout + * + * @param auth - Authentication token (JWT or installation access token) + * @returns Configured Octokit instance + */ + private createOctokit(auth: string): Octokit { + return new Octokit({ + auth, + request: { + timeout: GitHubService.DEFAULT_TIMEOUT, + headers: { + 'GraphQL-Features': 'issues_copilot_assignment_api_support', + }, + }, + }); + } + + /** + * Get private key from environment variables + * + * @returns {string} Private key in PEM format with real newlines + * @throws {Error} If GITHUB_PRIVATE_KEY is not set + */ + private getPrivateKey(): string { + if (process.env.GITHUB_PRIVATE_KEY) { + return normalizeGitHubPrivateKey(process.env.GITHUB_PRIVATE_KEY); + } + + throw new Error('GITHUB_PRIVATE_KEY must be set'); + } + + /** + * Create JWT token for GitHub App authentication + * + * @returns {string} JWT token + * @throws {Error} If GITHUB_APP_ID is not set + */ + private createJWT(): string { + if (!this.appId) { + throw new Error('GITHUB_APP_ID is required for GitHub App authentication'); + } + + const privateKey = this.getPrivateKey(); + const now = Math.floor(Date.now() / TimeMs.SECOND); + + /** + * JWT payload for GitHub App + * - iat: issued at time (current time) + * - exp: expiration time (10 minutes from now, GitHub allows up to 10 minutes) + * - iss: issuer (GitHub App ID) + */ + const secondsInMinute = TimeMs.MINUTE / TimeMs.SECOND; + const payload = { + iat: now - secondsInMinute, // Allow 1 minute clock skew + exp: now + (GitHubService.JWT_EXPIRATION_MINUTES * secondsInMinute), // 10 minutes expiration + iss: this.appId, + }; + + return jwt.sign(payload, privateKey, { algorithm: 'RS256' }); + } + + /** + * Get authentication token (installation access token) + * + * @param {string | null} installationId - GitHub App installation ID + * @returns {Promise} Authentication token + * @throws {Error} If token creation fails + */ + private async getAuthToken(installationId: string | null): Promise { + if (!installationId) { + throw new Error('installationId is required for GitHub App authentication'); + } + + console.log('[GitHub API] Using GitHub App authentication with installation ID:', installationId); + + return this.createInstallationToken(installationId); + } + + /** + * Get installation access token from GitHub API + * + * @param {string} installationId - GitHub App installation ID + * @returns {Promise} Installation access token (valid for 1 hour) + * @throws {Error} If token creation fails + */ + private async createInstallationToken(installationId: string): Promise { + const token = this.createJWT(); + + /** + * Create Octokit instance with JWT authentication and configured timeout + */ + const octokit = this.createOctokit(token); + + try { + /** + * Request installation access token + */ + // eslint-disable-next-line @typescript-eslint/naming-convention + const { data } = await octokit.rest.apps.createInstallationAccessToken({ + // eslint-disable-next-line @typescript-eslint/naming-convention + installation_id: parseInt(installationId, 10), + }); + + return data.token; + } catch (error) { + throw new Error(`Failed to create installation token: ${error instanceof Error ? error.message : String(error)}`); + } + } +} diff --git a/workers/task-manager/src/env.ts b/workers/task-manager/src/env.ts new file mode 100644 index 000000000..ad36e93ed --- /dev/null +++ b/workers/task-manager/src/env.ts @@ -0,0 +1,7 @@ +import * as path from 'path'; +import * as dotenv from 'dotenv'; + +/** + * Load environment variables from .env file + */ +dotenv.config({ path: path.resolve(__dirname, '../.env') }); diff --git a/workers/task-manager/src/index.ts b/workers/task-manager/src/index.ts new file mode 100644 index 000000000..d6dec0328 --- /dev/null +++ b/workers/task-manager/src/index.ts @@ -0,0 +1,690 @@ +import './env'; +import { ObjectId } from 'mongodb'; +import { DatabaseController } from '../../../lib/db/controller'; +import { Worker } from '../../../lib/worker'; +import * as pkg from '../package.json'; +import type { TaskManagerWorkerTask } from '../types/task-manager-worker-task'; +import type { + ProjectDBScheme, + GroupedEventDBScheme, + ProjectTaskManagerConfig +} from '@hawk.so/types'; +import type { TaskManagerItem } from '@hawk.so/types/src/base/event/taskManagerItem'; +import HawkCatcher from '@hawk.so/nodejs'; +import { decodeUnsafeFields } from '../../../lib/utils/unsafeFields'; +import { GitHubService } from './GithubService'; +import { formatIssueFromEvent } from './utils/issue'; +import TimeMs from '../../../lib/utils/time'; + +/** + * Default maximum number of auto-created tasks per project per day + */ +const DEFAULT_MAX_AUTO_TASKS_PER_DAY = 10; + +/** + * Maximum number of auto-created tasks per project per day + */ +const MAX_AUTO_TASKS_PER_DAY = parseInt(process.env.MAX_AUTO_TASKS_PER_DAY, 10) || DEFAULT_MAX_AUTO_TASKS_PER_DAY; + +/** + * Worker for automatically creating GitHub issues for errors that meet the threshold + */ +export default class TaskManagerWorker extends Worker { + /** + * Worker type + */ + public readonly type: string = pkg.workerType; + + /** + * Database Controller for accounts database + */ + private accountsDb: DatabaseController = new DatabaseController(process.env.MONGO_ACCOUNTS_DATABASE_URI); + + /** + * Database Controller for events database + */ + private eventsDb: DatabaseController = new DatabaseController(process.env.MONGO_EVENTS_DATABASE_URI); + + /** + * GitHub Service for creating issues + */ + private githubService: GitHubService = new GitHubService(); + + /** + * Start consuming messages + */ + public async start(): Promise { + await this.accountsDb.connect(); + await this.eventsDb.connect(); + + await super.start(); + this.handle({ type: 'auto-task-creation' }); + } + + /** + * Finish everything + */ + public async finish(): Promise { + await this.accountsDb.close(); + await this.eventsDb.close(); + await super.finish(); + } + + /** + * Task handling function + * + * @param task - task manager task to handle + */ + public async handle(task: TaskManagerWorkerTask): Promise { + try { + this.logger.info('Starting task manager worker', { taskType: task.type }); + + /** + * Get all projects with GitHub task manager enabled + */ + const projects = await this.getProjectsWithTaskManager(); + + this.logger.info(`Found ${projects.length} projects with task manager enabled`); + + /** + * Process each project + */ + for (const project of projects) { + await this.processProject(project); + } + + this.logger.info('Task manager worker completed'); + } catch (error) { + this.logger.error('Failed to handle task manager task:', error); + + HawkCatcher.send(error as Error, { + taskType: task.type, + }); + } + } + + /** + * Get all projects with task manager enabled + * + * @returns Promise with array of projects + */ + private async getProjectsWithTaskManager(): Promise { + const connection = await this.accountsDb.getConnection(); + const projectsCollection = connection.collection('projects'); + + const projects = await projectsCollection.find({ + 'taskManager.type': 'github', + 'taskManager.autoTaskEnabled': true, + 'taskManager.config.repoId': { + $exists: true, + $ne: null, + }, + 'taskManager.config.repoFullName': { + $exists: true, + $ne: null, + }, + }).toArray(); + + return projects; + } + + /** + * Process a single project + * + * @param project - project to process + */ + private async processProject(project: ProjectDBScheme): Promise { + const projectId = project._id.toString(); + const taskManager = project.taskManager as ProjectTaskManagerConfig; + + if (!taskManager) { + this.logger.warn(`Project ${projectId} has no task manager config`); + + return; + } + + this.logger.info(`Processing project ${projectId}`, { + repoFullName: taskManager.config.repoFullName, + threshold: taskManager.taskThresholdTotalCount, + }); + + /** + * Calculate day start UTC for today + */ + const now = new Date(); + const dayStartUtc = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(), 0, 0, 0, 0)); + + /** + * Check and reset usage if dayStartUtc differs + */ + const usage = taskManager.usage; + const shouldResetUsage = !usage || usage.dayStartUtc.getTime() !== dayStartUtc.getTime(); + + let currentUsage: { autoTasksCreated: number }; + + if (shouldResetUsage) { + this.logger.info(`Resetting usage for project ${projectId}`, { + oldDayStartUtc: usage?.dayStartUtc, + newDayStartUtc: dayStartUtc, + }); + + await this.resetUsage(projectId, dayStartUtc); + + /** + * After reset, usage is 0 + */ + currentUsage = { autoTasksCreated: 0 }; + } else { + /** + * Use usage from already loaded project + */ + currentUsage = { + autoTasksCreated: usage.autoTasksCreated || 0, + }; + } + + /** + * Check if budget is available + */ + if (currentUsage.autoTasksCreated >= MAX_AUTO_TASKS_PER_DAY) { + this.logger.info(`Project ${projectId} has reached daily budget limit`, { + autoTasksCreated: currentUsage.autoTasksCreated, + maxAutoTasksPerDay: MAX_AUTO_TASKS_PER_DAY, + }); + + return; + } + + /** + * Calculate remaining budget + */ + const remainingBudget = MAX_AUTO_TASKS_PER_DAY - currentUsage.autoTasksCreated; + + this.logger.info(`Project ${projectId} has remaining budget`, { + autoTasksCreated: currentUsage.autoTasksCreated, + remainingBudget, + }); + + /** + * Find events that need task creation + */ + const events = await this.findEventsForTaskCreation( + projectId, + taskManager.connectedAt, + taskManager.taskThresholdTotalCount + ); + + this.logger.info(`Found ${events.length} events for task creation in project ${projectId}`); + + /** + * Process events up to remaining budget + */ + const eventsToProcess = events.slice(0, remainingBudget); + + for (const event of eventsToProcess) { + await this.processEventForAutoTaskCreation({ + project, + projectId, + taskManager, + event, + dayStartUtc, + }); + } + } + + /** + * Process a single event for auto task creation + * + * @param params - method params + * @param params.project - project + * @param params.projectId - project id + * @param params.taskManager - task manager config + * @param params.event - grouped event + * @param params.dayStartUtc - day start UTC used for usage increment + */ + private async processEventForAutoTaskCreation(params: { + project: ProjectDBScheme; + projectId: string; + taskManager: ProjectTaskManagerConfig; + event: GroupedEventDBScheme; + dayStartUtc: Date; + }): Promise { + const { project, projectId, taskManager, event, dayStartUtc } = params; + + /** + * Format Issue data from event + */ + const issueData = formatIssueFromEvent(event, project); + + /** + * Step 1: Create GitHub Issue using installation token (GitHub App) + */ + // eslint-disable-next-line @typescript-eslint/naming-convention + let githubIssue: { number: number; html_url: string }; + + try { + githubIssue = await this.githubService.createIssue( + taskManager.config.repoFullName, + taskManager.config.installationId, + issueData + ); + } catch (error) { + /** + * Log error message only, not the full error object to avoid logging tokens + */ + this.logger.error(`Failed to create GitHub issue for event ${event.groupHash} (project ${projectId}): ${error instanceof Error ? error.message : String(error)}`); + + /** + * Do not increment usage and do not save taskManagerItem if issue creation failed + */ + return; + } + + /** + * Atomically increment usage.autoTasksCreated (only after successful issue creation) + */ + const incrementSuccess = await this.incrementAutoTasksCreated(projectId, dayStartUtc); + + if (!incrementSuccess) { + this.logger.warn( + `Issue #${githubIssue.number} was created but usage increment failed for project ${projectId} (budget may be exhausted)` + ); + + /** + * We still link the created issue to the event to avoid duplicates. + */ + } + + /** + * Save taskManagerItem to event (independent of Copilot assignment) + * We determine assignee status based on whether assignAgent is enabled, + * not whether assignment actually succeeded + */ + let copilotAssigned = false; + + /** + * Step 2: Assign Copilot agent if enabled (using user-to-server OAuth token) + */ + if (taskManager.assignAgent && taskManager.config.delegatedUser?.accessToken) { + try { + await this.executeWithTokenRefresh({ + projectId, + taskManager, + operationName: 'assign Copilot', + operation: async (token) => { + await this.githubService.assignCopilot( + taskManager.config.repoFullName, + githubIssue.number, + token + ); + }, + }); + copilotAssigned = true; + } catch (error) { + /** + * Log error but don't fail the whole operation - issue was created successfully + */ + this.logger.warn(`Failed to assign Copilot to issue #${githubIssue.number}: ${error instanceof Error ? error.message : String(error)}`); + copilotAssigned = false; + } + } + + /** + * Save taskManagerItem to event + * Note: assignee is set based on whether assignment was attempted and succeeded, + * not just whether assignAgent is enabled + */ + await this.saveTaskManagerItem( + projectId, + event, + githubIssue.number, + taskManager, + githubIssue.html_url, + copilotAssigned + ); + + this.logger.info(`Created task for event ${event.groupHash} in project ${projectId}`, { + issueNumber: githubIssue.number, + issueUrl: githubIssue.html_url, + assignAgent: taskManager.assignAgent, + }); + } + + /** + * Update delegatedUser tokens in project + * + * @param {string} projectId - Project ID + * @param {Object} tokenData - New token data + * @returns {Promise} + */ + private async updateDelegatedUserTokens( + projectId: string, + tokenData: { + accessToken: string; + refreshToken: string; + expiresAt: Date | null; + refreshTokenExpiresAt: Date | null; + tokenLastValidatedAt: Date; + } + ): Promise { + const connection = await this.accountsDb.getConnection(); + const projectsCollection = connection.collection('projects'); + + await projectsCollection.updateOne( + { _id: new ObjectId(projectId) }, + { + $set: { + 'taskManager.config.delegatedUser.accessToken': tokenData.accessToken, + 'taskManager.config.delegatedUser.refreshToken': tokenData.refreshToken, + 'taskManager.config.delegatedUser.accessTokenExpiresAt': tokenData.expiresAt, + 'taskManager.config.delegatedUser.refreshTokenExpiresAt': tokenData.refreshTokenExpiresAt, + 'taskManager.config.delegatedUser.tokenLastValidatedAt': tokenData.tokenLastValidatedAt, + 'taskManager.updatedAt': new Date(), + }, + } + ); + } + + /** + * Execute a GitHub API operation with automatic token refresh on 401 errors + * This function handles token refresh and retry logic for operations that may fail with 401 + * + * @param {Object} params - Parameters for the operation + * @param {string} params.projectId - Project ID + * @param {ProjectTaskManagerConfig} params.taskManager - Task manager configuration + * @param {Function} params.operation - Async function that performs the GitHub API operation + * @param {string} params.operationName - Name of the operation for logging (e.g., "create issue") + * @returns {Promise} Result of the operation + * @throws {Error} If operation fails and token refresh doesn't help + */ + private async executeWithTokenRefresh(params: { + projectId: string; + taskManager: ProjectTaskManagerConfig; + operation: (token: string | null) => Promise; + operationName: string; + }): Promise { + const { projectId, taskManager, operation, operationName } = params; + + /** + * Get valid access token with automatic refresh if needed + */ + let delegatedUserToken: string | null = null; + + if (taskManager.config.delegatedUser?.status === 'active') { + const delegatedUser = taskManager.config.delegatedUser; + + try { + /** + * Get valid access token with automatic refresh if needed + */ + delegatedUserToken = await this.githubService.getValidAccessToken( + { + accessToken: delegatedUser.accessToken, + refreshToken: delegatedUser.refreshToken, + accessTokenExpiresAt: delegatedUser.accessTokenExpiresAt + ? new Date(delegatedUser.accessTokenExpiresAt) + : null, + refreshTokenExpiresAt: delegatedUser.refreshTokenExpiresAt + ? new Date(delegatedUser.refreshTokenExpiresAt) + : null, + }, + /** + * Callback to save refreshed tokens in database + * + * @param newTokens + */ + async (newTokens) => { + await this.updateDelegatedUserTokens(projectId, { + ...newTokens, + tokenLastValidatedAt: new Date(), + }); + + this.logger.info(`Refreshed and saved new tokens for project ${projectId}`); + } + ); + } catch (refreshError) { + /** + * Log error message only, not the full error object to avoid logging tokens + */ + this.logger.warn(`Failed to refresh token for project ${projectId}, falling back to installation token: ${refreshError instanceof Error ? refreshError.message : String(refreshError)}`); + + /** + * If refresh fails, fall back to installation token + */ + delegatedUserToken = null; + } + } + + /** + * Try to execute the operation + */ + try { + return await operation(delegatedUserToken); + } catch (error) { + /** + * Check if error is 401 (unauthorized) - token might be revoked + * Try to refresh token and retry once + */ + const HTTP_UNAUTHORIZED = 401; + + if (error?.status === HTTP_UNAUTHORIZED && taskManager.config.delegatedUser?.status === 'active') { + const delegatedUser = taskManager.config.delegatedUser; + + this.logger.warn(`Received 401 error for project ${projectId} during ${operationName}, attempting token refresh...`); + + try { + /** + * Refresh token + */ + const newTokens = await this.githubService.refreshUserToken(delegatedUser.refreshToken); + + /** + * Save refreshed tokens + */ + await this.updateDelegatedUserTokens(projectId, { + ...newTokens, + tokenLastValidatedAt: new Date(), + }); + + /** + * Retry operation with new token + */ + const result = await operation(newTokens.accessToken); + + this.logger.info(`Successfully refreshed token and completed ${operationName} for project ${projectId}`); + + return result; + } catch (refreshError) { + /** + * Refresh failed, mark token as revoked + * Log error message only, not the full error object to avoid logging tokens + */ + this.logger.error(`Failed to refresh token for project ${projectId}: ${refreshError instanceof Error ? refreshError.message : String(refreshError)}`); + + await this.markDelegatedUserAsRevoked(projectId); + + /** + * Re-throw original error + */ + throw error; + } + } else { + /** + * Not a 401 error or no delegatedUser, re-throw original error + */ + throw error; + } + } + } + + /** + * Mark delegatedUser as revoked + * + * @param {string} projectId - Project ID + * @returns {Promise} + */ + private async markDelegatedUserAsRevoked(projectId: string): Promise { + const connection = await this.accountsDb.getConnection(); + const projectsCollection = connection.collection('projects'); + + await projectsCollection.updateOne( + { _id: new ObjectId(projectId) }, + { + $set: { + 'taskManager.config.delegatedUser.status': 'revoked', + 'taskManager.updatedAt': new Date(), + }, + } + ); + } + + /** + * Reset usage for a project + * + * @param projectId - project ID + * @param dayStartUtc - new day start UTC + */ + private async resetUsage(projectId: string, dayStartUtc: Date): Promise { + const connection = await this.accountsDb.getConnection(); + const projectsCollection = connection.collection('projects'); + + await projectsCollection.updateOne( + { _id: new ObjectId(projectId) }, + { + $set: { + 'taskManager.usage': { + dayStartUtc, + autoTasksCreated: 0, + }, + }, + } + ); + } + + /** + * Atomically increment autoTasksCreated + * + * @param projectId - project ID + * @param dayStartUtc - day start UTC + * @returns Promise with true if increment was successful, false if budget exhausted + */ + private async incrementAutoTasksCreated(projectId: string, dayStartUtc: Date): Promise { + const connection = await this.accountsDb.getConnection(); + const projectsCollection = connection.collection('projects'); + + /** + * Use findOneAndUpdate with condition to atomically increment + * Only increment if autoTasksCreated < MAX_AUTO_TASKS_PER_DAY + */ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const result = await (projectsCollection.findOneAndUpdate( + { + _id: new ObjectId(projectId), + 'taskManager.usage.dayStartUtc': dayStartUtc, + $or: [ + { 'taskManager.usage.autoTasksCreated': { $exists: false } }, + { 'taskManager.usage.autoTasksCreated': { $lt: MAX_AUTO_TASKS_PER_DAY } }, + ], + }, + { + $inc: { 'taskManager.usage.autoTasksCreated': 1 }, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any, + { + returnDocument: 'after', + } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ) as any); + + return result?.value !== null && result?.value !== undefined; + } + + /** + * Find events that need task creation + * + * @param projectId - project ID + * @param connectedAt - task manager connection date + * @param threshold - minimum totalCount threshold + * @returns Promise with array of events + */ + private async findEventsForTaskCreation( + projectId: string, + connectedAt: Date, + threshold: number + ): Promise { + const connection = await this.eventsDb.getConnection(); + const eventsCollection = connection.collection(`events:${projectId}`); + + /** + * Convert connectedAt to timestamp (seconds) + */ + const connectedAtTimestamp = Math.floor(connectedAt.getTime() / TimeMs.SECOND); + + const events = await eventsCollection + .find({ + taskManagerItem: { $exists: false }, + timestamp: { $gte: connectedAtTimestamp }, + totalCount: { $gte: threshold }, + }) + .sort({ + totalCount: -1, + timestamp: -1, + }) + .toArray(); + + return events; + } + + /** + * Save taskManagerItem to event + * + * @param projectId - project ID + * @param event - event to save taskManagerItem to + * @param issueNumber - GitHub issue number + * @param taskManager - task manager config + * @param issueUrl - GitHub issue URL + * @param copilotAssigned - whether Copilot was successfully assigned + */ + private async saveTaskManagerItem( + projectId: string, + event: GroupedEventDBScheme, + issueNumber: number, + taskManager: ProjectTaskManagerConfig, + issueUrl: string, + copilotAssigned = false + ): Promise { + const connection = await this.eventsDb.getConnection(); + const eventsCollection = connection.collection(`events:${projectId}`); + + /** + * Decode unsafe fields to get actual title + */ + const decodedEvent = { ...event }; + + decodeUnsafeFields(decodedEvent); + + const taskManagerItem: TaskManagerItem = { + type: 'github-issue', + number: issueNumber, + url: issueUrl, + title: decodedEvent.payload.title, + createdBy: 'auto', + createdAt: new Date(), + assignee: copilotAssigned ? 'copilot' : null, + }; + + await eventsCollection.updateOne( + { _id: event._id }, + { + $set: { + taskManagerItem, + }, + } + ); + + this.logger.info(`Saved taskManagerItem for event ${event.groupHash}`, { + issueNumber, + url: taskManagerItem.url, + }); + } +} diff --git a/workers/task-manager/src/utils/githubPrivateKey.ts b/workers/task-manager/src/utils/githubPrivateKey.ts new file mode 100644 index 000000000..7dfd6872a --- /dev/null +++ b/workers/task-manager/src/utils/githubPrivateKey.ts @@ -0,0 +1,58 @@ +/** + * Normalize and validate GitHub App private key. + * + * @param rawPrivateKey - raw value from env (GITHUB_PRIVATE_KEY) + * @returns PEM-encoded private key string + */ +export function normalizeGitHubPrivateKey(rawPrivateKey: string): string { + /** + * Trim and remove surrounding quotes (dotenv can keep them) + */ + let privateKey = rawPrivateKey.trim(); + + if ( + (privateKey.startsWith('"') && privateKey.endsWith('"')) || + (privateKey.startsWith('\'') && privateKey.endsWith('\'')) + ) { + privateKey = privateKey.slice(1, -1); + } + + /** + * Support passing base64-encoded private key (common in CI). + * If it doesn't look like a PEM block but looks like base64, decode it. + */ + const MIN_BASE64_KEY_LENGTH = 200; + + if (!privateKey.includes('BEGIN') && /^[A-Za-z0-9+/=\s]+$/.test(privateKey) && privateKey.length > MIN_BASE64_KEY_LENGTH) { + try { + privateKey = Buffer.from(privateKey, 'base64').toString('utf8'); + } catch { + /** + * Keep original value, we'll validate below. + */ + } + } + + /** + * Replace literal "\n" sequences with actual newlines. + */ + if (privateKey.includes('\\n') && !privateKey.includes('\n')) { + privateKey = privateKey.replace(/\\n/g, '\n'); + } + + /** + * Normalize Windows line endings if any. + */ + privateKey = privateKey.replace(/\r\n/g, '\n'); + + /** + * Basic validation: must be a PEM private key. + */ + if (!privateKey.includes('-----BEGIN') || !privateKey.includes('PRIVATE KEY-----')) { + throw new Error( + 'GITHUB_PRIVATE_KEY must be a valid PEM-encoded private key (-----BEGIN ... PRIVATE KEY----- ... -----END ... PRIVATE KEY-----)' + ); + } + + return privateKey; +} diff --git a/workers/task-manager/src/utils/issue.ts b/workers/task-manager/src/utils/issue.ts new file mode 100644 index 000000000..c6298edc7 --- /dev/null +++ b/workers/task-manager/src/utils/issue.ts @@ -0,0 +1,260 @@ +import type { GroupedEventDBScheme, ProjectDBScheme } from '@hawk.so/types'; +import { decodeUnsafeFields } from '../../../../lib/utils/unsafeFields'; +import TimeMs from '../../../../lib/utils/time'; +import type { IssueData } from '../GithubService'; + +/** + * Width used for padding date/time parts. + */ +const DATE_TIME_PART_WIDTH = 2; + +/** + * Number of spaces used for JSON pretty-printing. + */ +const JSON_INDENT_SPACES = 2; + +/** + * Format date for display in GitHub issue + * + * @param timestamp - Unix timestamp in seconds + * @returns {string} Formatted date string (e.g., "23 Feb 2025 14:40:21") + */ +function formatDate(timestamp: number): string { + const date = new Date(timestamp * TimeMs.SECOND); + const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; + const day = date.getUTCDate(); + const month = months[date.getUTCMonth()]; + const year = date.getUTCFullYear(); + const hours = date.getUTCHours().toString() + .padStart(DATE_TIME_PART_WIDTH, '0'); + const minutes = date.getUTCMinutes().toString() + .padStart(DATE_TIME_PART_WIDTH, '0'); + const seconds = date.getUTCSeconds().toString() + .padStart(DATE_TIME_PART_WIDTH, '0'); + + return `${day} ${month} ${year} ${hours}:${minutes}:${seconds}`; +} + +/** + * Calculate days repeating from timestamp + * + * @param timestamp - Unix timestamp in seconds + * @returns {number} Number of days since first occurrence + */ +function calculateDaysRepeating(timestamp: number): number { + const now = Date.now(); + const eventTimestamp = timestamp * TimeMs.SECOND; + const differenceInDays = (now - eventTimestamp) / TimeMs.DAY; + + return Math.round(differenceInDays); +} + +/** + * Format source code as diff with line numbers + * The error line is marked with minus sign, other lines with space + * + * @param sourceCode - Array of source code lines + * @param errorLine - Line number where error occurred + * @returns {string} Formatted diff string + */ +function formatSourceCodeAsDiff(sourceCode: Array<{ line: number; content: string }>, errorLine: number): string { + const lines: string[] = []; + + /** + * Use the widest line number among provided source lines and error line. + * This keeps alignment correct for both small and large line numbers. + */ + const maxLineNumber = sourceCode.reduce((maxLine, current) => { + if (current.line > maxLine) { + return current.line; + } + + return maxLine; + }, errorLine); + const lineNumberWidth = String(maxLineNumber).length; + + for (const sourceLine of sourceCode) { + const lineNumber = sourceLine.line.toString().padStart(lineNumberWidth, ' '); + const isErrorLine = sourceLine.line === errorLine; + const prefix = isErrorLine ? '-' : ' '; + + /** + * Do not escape HTML here because content is rendered inside Markdown code block. + */ + const content = sourceLine.content ?? ''; + + lines.push(`${prefix}${lineNumber}: ${content}`); + } + + return lines.join('\n'); +} + +/** + * Format GitHub Issue from event + * + * @param event - event to format issue for + * @param project - project + * @returns {IssueData} Issue data for GitHub API + */ +export function formatIssueFromEvent(event: GroupedEventDBScheme, project: ProjectDBScheme): IssueData { + /** + * Decode unsafe fields (context, addons) if they are strings + */ + const decodedEvent = { ...event }; + + decodeUnsafeFields(decodedEvent); + + const projectId = project._id.toString(); + const garageUrl = process.env.GARAGE_URL || 'https://garage.hawk.so'; + const eventUrl = `${garageUrl}/project/${projectId}/event/${event.groupHash}`; + + /** + * Format title: [Hawk] ${event.payload.title} + */ + const title = `[Hawk] ${decodedEvent.payload.title}`; + + /** + * Format body according to the template: + * - H2 header + * - Stacktrace with first frame expanded, others in details + * - Table with event data + * - Context and Addons as JSON + * - Link to event in Hawk + */ + const bodyParts: string[] = []; + + /** + * H2 header with title + */ + bodyParts.push(`## ${decodedEvent.payload.title}`); + + /** + * Stacktrace section + */ + if (decodedEvent.payload.backtrace && decodedEvent.payload.backtrace.length > 0) { + const firstFrame = decodedEvent.payload.backtrace[0]; + const file = firstFrame.file || ''; + const line = firstFrame.line || 0; + const column = firstFrame.column || 0; + const func = firstFrame.function || ''; + + /** + * First frame - always visible + */ + bodyParts.push(`\n- at ${func} (${file}:${line}:${column})`); + + /** + * Source code for first frame in diff format + */ + if (firstFrame.sourceCode && firstFrame.sourceCode.length > 0) { + bodyParts.push('\n```diff'); + bodyParts.push(formatSourceCodeAsDiff(firstFrame.sourceCode, line)); + bodyParts.push('```'); + } + + /** + * Additional frames in details section + */ + if (decodedEvent.payload.backtrace.length > 1) { + bodyParts.push('\n
'); + bodyParts.push(' View full stack trace'); + bodyParts.push(' \n'); + + for (let i = 1; i < decodedEvent.payload.backtrace.length; i++) { + const frame = decodedEvent.payload.backtrace[i]; + const frameFile = frame.file || ''; + const frameLine = frame.line || 0; + const frameColumn = frame.column || 0; + const frameFunc = frame.function || ''; + + bodyParts.push(`- at ${frameFunc} (${frameFile}:${frameLine}:${frameColumn})`); + + /** + * Source code for this frame in diff format + */ + if (frame.sourceCode && frame.sourceCode.length > 0) { + bodyParts.push('\n```diff'); + bodyParts.push(formatSourceCodeAsDiff(frame.sourceCode, frameLine)); + bodyParts.push('```'); + } + + /** + * Add newline between frames if not last + */ + if (i < decodedEvent.payload.backtrace.length - 1) { + bodyParts.push(''); + } + } + + bodyParts.push('\n
'); + } + } + + /** + * Table with event data + */ + const sinceDate = formatDate(decodedEvent.timestamp); + const daysRepeating = calculateDaysRepeating(decodedEvent.timestamp); + + bodyParts.push('\n| Param | Value |'); + bodyParts.push('| -- | :--: |'); + bodyParts.push(`| Since | ${sinceDate} |`); + bodyParts.push(`| Days Repeating | ${daysRepeating} |`); + bodyParts.push(`| Total Occurrences | ${decodedEvent.totalCount} |`); + bodyParts.push(`| Users Affected | ${decodedEvent.usersAffected || '-'} |`); + + /** + * Context and Addons sections in details + */ + if (decodedEvent.payload.context || decodedEvent.payload.addons) { + bodyParts.push('\n
'); + bodyParts.push(' View Context and Addons'); + bodyParts.push(' \n'); + + /** + * Context section + */ + if (decodedEvent.payload.context) { + bodyParts.push('### Context'); + bodyParts.push('\n```json'); + bodyParts.push(JSON.stringify(decodedEvent.payload.context, null, JSON_INDENT_SPACES)); + bodyParts.push('```'); + } + + /** + * Addons section + */ + if (decodedEvent.payload.addons) { + bodyParts.push('\n### Addons'); + bodyParts.push('\n```json'); + bodyParts.push(JSON.stringify(decodedEvent.payload.addons, null, JSON_INDENT_SPACES)); + bodyParts.push('```'); + } + + bodyParts.push('\n
'); + } + + /** + * Link to event in Hawk + */ + bodyParts.push('\n### Details'); + bodyParts.push(`\n[View in Hawk](${eventUrl})`); + + /** + * Technical marker for tracking + */ + bodyParts.push(`\n\n`); + + const body = bodyParts.join('\n'); + + /** + * Labels: hawk:error + */ + const labels = [ 'hawk:error' ]; + + return { + title, + body, + labels, + }; +} diff --git a/workers/task-manager/types/task-manager-worker-task.ts b/workers/task-manager/types/task-manager-worker-task.ts new file mode 100644 index 000000000..e2a81e53c --- /dev/null +++ b/workers/task-manager/types/task-manager-worker-task.ts @@ -0,0 +1,11 @@ +import { WorkerTask } from '../../../lib/types/worker-task'; + +/** + * TaskManagerWorker task description + */ +export interface TaskManagerWorkerTask extends WorkerTask { + /** + * Task type + */ + type: 'auto-task-creation'; +} diff --git a/yarn.lock b/yarn.lock index 3fccdbef8..2232af0ad 100644 --- a/yarn.lock +++ b/yarn.lock @@ -714,6 +714,155 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" +"@octokit/auth-token@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-6.0.0.tgz#b02e9c08a2d8937df09a2a981f226ad219174c53" + integrity sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w== + +"@octokit/core@^7.0.6": + version "7.0.6" + resolved "https://registry.yarnpkg.com/@octokit/core/-/core-7.0.6.tgz#0d58704391c6b681dec1117240ea4d2a98ac3916" + integrity sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q== + dependencies: + "@octokit/auth-token" "^6.0.0" + "@octokit/graphql" "^9.0.3" + "@octokit/request" "^10.0.6" + "@octokit/request-error" "^7.0.2" + "@octokit/types" "^16.0.0" + before-after-hook "^4.0.0" + universal-user-agent "^7.0.0" + +"@octokit/endpoint@^11.0.2": + version "11.0.2" + resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-11.0.2.tgz#a8d955e053a244938b81d86cd73efd2dcb5ef5af" + integrity sha512-4zCpzP1fWc7QlqunZ5bSEjxc6yLAlRTnDwKtgXfcI/FxxGoqedDG8V2+xJ60bV2kODqcGB+nATdtap/XYq2NZQ== + dependencies: + "@octokit/types" "^16.0.0" + universal-user-agent "^7.0.2" + +"@octokit/endpoint@^9.0.6": + version "9.0.6" + resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-9.0.6.tgz#114d912108fe692d8b139cfe7fc0846dfd11b6c0" + integrity sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw== + dependencies: + "@octokit/types" "^13.1.0" + universal-user-agent "^6.0.0" + +"@octokit/graphql@^9.0.3": + version "9.0.3" + resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-9.0.3.tgz#5b8341c225909e924b466705c13477face869456" + integrity sha512-grAEuupr/C1rALFnXTv6ZQhFuL1D8G5y8CN04RgrO4FIPMrtm+mcZzFG7dcBm+nq+1ppNixu+Jd78aeJOYxlGA== + dependencies: + "@octokit/request" "^10.0.6" + "@octokit/types" "^16.0.0" + universal-user-agent "^7.0.0" + +"@octokit/oauth-authorization-url@^6.0.2": + version "6.0.2" + resolved "https://registry.yarnpkg.com/@octokit/oauth-authorization-url/-/oauth-authorization-url-6.0.2.tgz#cc82ca29cc5e339c9921672f39f2b3f5c8eb6ef2" + integrity sha512-CdoJukjXXxqLNK4y/VOiVzQVjibqoj/xHgInekviUJV73y/BSIcwvJ/4aNHPBPKcPWFnd4/lO9uqRV65jXhcLA== + +"@octokit/oauth-methods@^4.0.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@octokit/oauth-methods/-/oauth-methods-4.1.0.tgz#1403ac9c4d4e277922fddc4c89fa8a782f8f791b" + integrity sha512-4tuKnCRecJ6CG6gr0XcEXdZtkTDbfbnD5oaHBmLERTjTMZNi2CbfEHZxPU41xXLDG4DfKf+sonu00zvKI9NSbw== + dependencies: + "@octokit/oauth-authorization-url" "^6.0.2" + "@octokit/request" "^8.3.1" + "@octokit/request-error" "^5.1.0" + "@octokit/types" "^13.0.0" + btoa-lite "^1.0.0" + +"@octokit/openapi-types@^24.2.0": + version "24.2.0" + resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-24.2.0.tgz#3d55c32eac0d38da1a7083a9c3b0cca77924f7d3" + integrity sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg== + +"@octokit/openapi-types@^27.0.0": + version "27.0.0" + resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-27.0.0.tgz#374ea53781965fd02a9d36cacb97e152cefff12d" + integrity sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA== + +"@octokit/plugin-paginate-rest@^14.0.0": + version "14.0.0" + resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-14.0.0.tgz#44dc9fff2dacb148d4c5c788b573ddc044503026" + integrity sha512-fNVRE7ufJiAA3XUrha2omTA39M6IXIc6GIZLvlbsm8QOQCYvpq/LkMNGyFlB1d8hTDzsAXa3OKtybdMAYsV/fw== + dependencies: + "@octokit/types" "^16.0.0" + +"@octokit/plugin-request-log@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-6.0.0.tgz#de1c1e557df6c08adb631bf78264fa741e01b317" + integrity sha512-UkOzeEN3W91/eBq9sPZNQ7sUBvYCqYbrrD8gTbBuGtHEuycE4/awMXcYvx6sVYo7LypPhmQwwpUe4Yyu4QZN5Q== + +"@octokit/plugin-rest-endpoint-methods@^17.0.0": + version "17.0.0" + resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-17.0.0.tgz#8c54397d3a4060356a1c8a974191ebf945924105" + integrity sha512-B5yCyIlOJFPqUUeiD0cnBJwWJO8lkJs5d8+ze9QDP6SvfiXSz1BF+91+0MeI1d2yxgOhU/O+CvtiZ9jSkHhFAw== + dependencies: + "@octokit/types" "^16.0.0" + +"@octokit/request-error@^5.1.0", "@octokit/request-error@^5.1.1": + version "5.1.1" + resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-5.1.1.tgz#b9218f9c1166e68bb4d0c89b638edc62c9334805" + integrity sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g== + dependencies: + "@octokit/types" "^13.1.0" + deprecation "^2.0.0" + once "^1.4.0" + +"@octokit/request-error@^7.0.2": + version "7.1.0" + resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-7.1.0.tgz#440fa3cae310466889778f5a222b47a580743638" + integrity sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw== + dependencies: + "@octokit/types" "^16.0.0" + +"@octokit/request@^10.0.6": + version "10.0.7" + resolved "https://registry.yarnpkg.com/@octokit/request/-/request-10.0.7.tgz#93f619914c523750a85e7888de983e1009eb03f6" + integrity sha512-v93h0i1yu4idj8qFPZwjehoJx4j3Ntn+JhXsdJrG9pYaX6j/XRz2RmasMUHtNgQD39nrv/VwTWSqK0RNXR8upA== + dependencies: + "@octokit/endpoint" "^11.0.2" + "@octokit/request-error" "^7.0.2" + "@octokit/types" "^16.0.0" + fast-content-type-parse "^3.0.0" + universal-user-agent "^7.0.2" + +"@octokit/request@^8.3.1": + version "8.4.1" + resolved "https://registry.yarnpkg.com/@octokit/request/-/request-8.4.1.tgz#715a015ccf993087977ea4365c44791fc4572486" + integrity sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw== + dependencies: + "@octokit/endpoint" "^9.0.6" + "@octokit/request-error" "^5.1.1" + "@octokit/types" "^13.1.0" + universal-user-agent "^6.0.0" + +"@octokit/rest@^22.0.1": + version "22.0.1" + resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-22.0.1.tgz#4d866c32b76b711d3f736f91992e2b534163b416" + integrity sha512-Jzbhzl3CEexhnivb1iQ0KJ7s5vvjMWcmRtq5aUsKmKDrRW6z3r84ngmiFKFvpZjpiU/9/S6ITPFRpn5s/3uQJw== + dependencies: + "@octokit/core" "^7.0.6" + "@octokit/plugin-paginate-rest" "^14.0.0" + "@octokit/plugin-request-log" "^6.0.0" + "@octokit/plugin-rest-endpoint-methods" "^17.0.0" + +"@octokit/types@^13.0.0", "@octokit/types@^13.1.0": + version "13.10.0" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-13.10.0.tgz#3e7c6b19c0236c270656e4ea666148c2b51fd1a3" + integrity sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA== + dependencies: + "@octokit/openapi-types" "^24.2.0" + +"@octokit/types@^16.0.0": + version "16.0.0" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-16.0.0.tgz#fbd7fa590c2ef22af881b1d79758bfaa234dbb7c" + integrity sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg== + dependencies: + "@octokit/openapi-types" "^27.0.0" + "@pkgjs/parseargs@^0.11.0": version "0.11.0" resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" @@ -1012,6 +1161,13 @@ resolved "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz" integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= +"@types/jsonwebtoken@^8.3.5": + version "8.5.9" + resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-8.5.9.tgz#2c064ecb0b3128d837d2764aa0b117b0ff6e4586" + integrity sha512-272FMnFGzAVMGtu9tkr29hRL6bZj4Zs1KZNeHLnKqAvp06tAIcarTMwOh8/8bz4FmKRcMxZhZNeUAQsNLoiPhg== + dependencies: + "@types/node" "*" + "@types/mongodb@^3.5.15", "@types/mongodb@^3.5.34": version "3.6.20" resolved "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.6.20.tgz" @@ -1739,6 +1895,11 @@ bcrypt-pbkdf@^1.0.2: dependencies: tweetnacl "^0.14.3" +before-after-hook@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-4.0.0.tgz#cf1447ab9160df6a40f3621da64d6ffc36050cb9" + integrity sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ== + binary-extensions@^2.0.0: version "2.2.0" resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz" @@ -1873,6 +2034,11 @@ bson@^7.0.0: resolved "https://registry.yarnpkg.com/bson/-/bson-7.1.1.tgz#19965d9138e1c4d88e4690414d91c84f217c84e8" integrity sha512-TtJgBB+QyOlWjrbM+8bRgH84VM/xrDjyBFgSgGrfZF4xvt6gbEDtcswm27Tn9F9TWsjQybxT8b8VpCP/oJK4Dw== +btoa-lite@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/btoa-lite/-/btoa-lite-1.0.0.tgz#337766da15801210fdd956c22e9c6891ab9d0337" + integrity sha512-gvW7InbIyF8AicrqWoptdW08pUxuhq8BEgowNajy9RhiE86fmGAGl+bLKo6oB8QP0CkqHLowfN0oJdKC/J6LbA== + buffer-crc32@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-1.0.0.tgz#a10993b9055081d55304bd9feb4a072de179f405" @@ -1883,6 +2049,11 @@ buffer-crc32@~0.2.3: resolved "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz" integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= +buffer-equal-constant-time@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" + integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA== + buffer-from@^1.0.0: version "1.1.2" resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz" @@ -2384,6 +2555,11 @@ denque@^1.4.1: resolved "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz" integrity sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw== +deprecation@^2.0.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" + integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== + detect-newline@^3.0.0: version "3.1.0" resolved "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz" @@ -2468,6 +2644,11 @@ dotenv@*: resolved "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz" integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== +dotenv@^17.2.3: + version "17.2.3" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-17.2.3.tgz#ad995d6997f639b11065f419a22fabf567cdb9a2" + integrity sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w== + dotenv@^8.2.0: version "8.6.0" resolved "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz" @@ -2483,6 +2664,13 @@ eastasianwidth@^0.2.0: resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== +ecdsa-sig-formatter@1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" + integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== + dependencies: + safe-buffer "^5.0.1" + electron-to-chromium@^1.5.263: version "1.5.279" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.279.tgz#67dfdeb22fd81412d0d18d1d9b2c749e9b8945cb" @@ -2883,6 +3071,11 @@ expect@^29.0.0, expect@^29.7.0: jest-message-util "^29.7.0" jest-util "^29.7.0" +fast-content-type-parse@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/fast-content-type-parse/-/fast-content-type-parse-3.0.0.tgz#5590b6c807cc598be125e6740a9fde589d2b7afb" + integrity sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg== + fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" @@ -4155,6 +4348,39 @@ jsonfile@^6.0.1: optionalDependencies: graceful-fs "^4.1.6" +jsonwebtoken@^9.0.3: + version "9.0.3" + resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz#6cd57ab01e9b0ac07cb847d53d3c9b6ee31f7ae2" + integrity sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g== + dependencies: + jws "^4.0.1" + lodash.includes "^4.3.0" + lodash.isboolean "^3.0.3" + lodash.isinteger "^4.0.4" + lodash.isnumber "^3.0.3" + lodash.isplainobject "^4.0.6" + lodash.isstring "^4.0.1" + lodash.once "^4.0.0" + ms "^2.1.1" + semver "^7.5.4" + +jwa@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/jwa/-/jwa-2.0.1.tgz#bf8176d1ad0cd72e0f3f58338595a13e110bc804" + integrity sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg== + dependencies: + buffer-equal-constant-time "^1.0.1" + ecdsa-sig-formatter "1.0.11" + safe-buffer "^5.0.1" + +jws@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/jws/-/jws-4.0.1.tgz#07edc1be8fac20e677b283ece261498bd38f0690" + integrity sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA== + dependencies: + jwa "^2.0.1" + safe-buffer "^5.0.1" + keyv@^3.0.0: version "3.1.0" resolved "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz" @@ -4269,11 +4495,21 @@ lodash.clonedeep@^4.5.0: resolved "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz" integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= +lodash.includes@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" + integrity sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w== + lodash.isboolean@^3.0.3: version "3.0.3" resolved "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz" integrity sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY= +lodash.isinteger@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" + integrity sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA== + lodash.isnumber@^3.0.3: version "3.0.3" resolved "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz" @@ -4284,6 +4520,11 @@ lodash.isobject@^3.0.2: resolved "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-3.0.2.tgz" integrity sha1-PI+41bW/S/kK4G4U8qUwpO2TXh0= +lodash.isplainobject@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" + integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA== + lodash.isstring@^4.0.1: version "4.0.1" resolved "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz" @@ -4314,6 +4555,11 @@ lodash.omitby@^4.6.0: resolved "https://registry.npmjs.org/lodash.omitby/-/lodash.omitby-4.6.0.tgz" integrity sha1-XBX/R1StVVAWtTwEExHo8HkgR5E= +lodash.once@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" + integrity sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg== + lodash.truncate@^4.4.2: version "4.4.2" resolved "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz" @@ -5477,7 +5723,7 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" -safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0, safe-buffer@~5.2.1: +safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0, safe-buffer@~5.2.1: version "5.2.1" resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -6282,6 +6528,16 @@ unique-string@^2.0.0: dependencies: crypto-random-string "^2.0.0" +universal-user-agent@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.1.tgz#15f20f55da3c930c57bddbf1734c6654d5fd35aa" + integrity sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ== + +universal-user-agent@^7.0.0, universal-user-agent@^7.0.2: + version "7.0.3" + resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-7.0.3.tgz#c05870a58125a2dc00431f2df815a77fe69736be" + integrity sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A== + universalify@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz" From be2aedc8fd89c1fd7e77f7811dc80d7562fed36a Mon Sep 17 00:00:00 2001 From: Kuchizu <70284260+Kuchizu@users.noreply.github.com> Date: Wed, 28 Jan 2026 13:38:26 +0300 Subject: [PATCH 08/20] Add PR Assistant workflow configuration (#507) Co-authored-by: Peter --- .github/workflows/pr-assistant.yml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 .github/workflows/pr-assistant.yml diff --git a/.github/workflows/pr-assistant.yml b/.github/workflows/pr-assistant.yml new file mode 100644 index 000000000..3f2608582 --- /dev/null +++ b/.github/workflows/pr-assistant.yml @@ -0,0 +1,27 @@ +name: PR Assistant + +on: + pull_request: + types: [opened, edited, synchronize] + +permissions: + pull-requests: write + issues: write + contents: write + +jobs: + pr-assistant: + runs-on: ubuntu-latest + steps: + - name: Generate GitHub App token + id: app-token + uses: actions/create-github-app-token@v2 + with: + app-id: ${{ secrets.CODEX_ASSISTANT_APP_ID }} + private-key: ${{ secrets.CODEX_ASSISTANT_PRIVATE_KEY }} + + - uses: codex-team/action-pr-assistant@master + with: + check: description + mode: draft + token: ${{ steps.app-token.outputs.token }} From 300b469f83fa50613a3b6ab2566967ea0cfb0913 Mon Sep 17 00:00:00 2001 From: Peter Savchenko Date: Wed, 28 Jan 2026 14:09:09 +0300 Subject: [PATCH 09/20] Update package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5b69d4e86..506d732d0 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "hawk.workers", "private": true, - "version": "0.1.0", + "version": "0.1.1", "description": "Hawk workers", "repository": "git@github.com:codex-team/hawk.workers.git", "license": "BUSL-1.1", From 4189b0a691589a84634b5031379dfd3151841a4b Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sat, 31 Jan 2026 05:24:30 +0500 Subject: [PATCH 10/20] Fix DataFilter false positives for UUIDs and MongoDB ObjectIds (#518) * Initial plan * Fix DataFilter to not filter UUIDs and MongoDB ObjectIds Co-authored-by: neSpecc <3684889+neSpecc@users.noreply.github.com> * Address code review feedback: improve UUID regex and test coverage Co-authored-by: neSpecc <3684889+neSpecc@users.noreply.github.com> * Fix tests to use values that would actually fail without UUID/ObjectId detection Co-authored-by: neSpecc <3684889+neSpecc@users.noreply.github.com> * upd version --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: neSpecc <3684889+neSpecc@users.noreply.github.com> Co-authored-by: Peter Savchenko --- package.json | 2 +- workers/grouper/package.json | 2 +- workers/grouper/src/data-filter.ts | 26 +++++ workers/grouper/tests/data-filter.test.ts | 119 ++++++++++++++++++++++ 4 files changed, 147 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 506d732d0..a053c5b87 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "hawk.workers", "private": true, - "version": "0.1.1", + "version": "0.1.2", "description": "Hawk workers", "repository": "git@github.com:codex-team/hawk.workers.git", "license": "BUSL-1.1", diff --git a/workers/grouper/package.json b/workers/grouper/package.json index 6e98edc11..294322a58 100644 --- a/workers/grouper/package.json +++ b/workers/grouper/package.json @@ -1,6 +1,6 @@ { "name": "hawk-worker-grouper", - "version": "0.0.1", + "version": "0.0.2", "description": "Accepts processed errors from language-workers and saves it to the DB with grouping of similar ones. ", "main": "src/index.ts", "repository": "https://github.com/codex-team/hawk.workers/tree/master/workers/grouper", diff --git a/workers/grouper/src/data-filter.ts b/workers/grouper/src/data-filter.ts index 40a4acf93..3571a1c6d 100644 --- a/workers/grouper/src/data-filter.ts +++ b/workers/grouper/src/data-filter.ts @@ -54,6 +54,16 @@ export default class DataFilter { */ private bankCardRegex = /^(?:4[0-9]{12}(?:[0-9]{3})?|[25][1-7][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\d{3})\d{11})$/g; + /** + * MongoDB ObjectId Regex (24 hexadecimal characters) + */ + private objectIdRegex = /^[0-9a-fA-F]{24}$/; + + /** + * UUID Regex - matches UUIDs with all dashes (8-4-4-4-12 format) or no dashes (32 hex chars) + */ + private uuidRegex = /^(?:[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|[0-9a-fA-F]{32})$/; + /** * Accept event and process 'addons' and 'context' fields. * It mutates the original object @@ -96,6 +106,22 @@ export default class DataFilter { return value; } + /** + * Check if value matches MongoDB ObjectId pattern (24 hex chars) + * ObjectIds should not be filtered + */ + if (this.objectIdRegex.test(value)) { + return value; + } + + /** + * Check if value matches UUID pattern (with or without dashes) + * UUIDs should not be filtered + */ + if (this.uuidRegex.test(value)) { + return value; + } + /** * Remove all non-digit chars */ diff --git a/workers/grouper/tests/data-filter.test.ts b/workers/grouper/tests/data-filter.test.ts index d0a4c3af8..28ff2979a 100644 --- a/workers/grouper/tests/data-filter.test.ts +++ b/workers/grouper/tests/data-filter.test.ts @@ -143,5 +143,124 @@ describe('GrouperWorker', () => { expect(event.context['normalKey']).toBe(normalValue); expect(event.addons['vue']['props']['normalKey']).toBe(normalValue); }); + + test('should not filter UUID values that contain exactly 16 digits', async () => { + // These UUIDs contain exactly 16 digits, which when cleaned match PAN patterns + // Without UUID detection, they would be incorrectly filtered as credit cards + const uuidWithManyDigits = '4a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d'; // Cleans to 16 digits starting with 4 + const uuidUpperCase = '5A1B2C3D-4E5F-6A7B-8C9D-0E1F2A3B4C5D'; // Cleans to 16 digits starting with 5 + const uuidNoDashes = '2a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d'; // 32 hex chars without dashes + + const event = generateEvent({ + context: { + userId: uuidWithManyDigits, + sessionId: uuidUpperCase, + transactionId: uuidNoDashes, + }, + addons: { + vue: { + props: { + componentId: uuidWithManyDigits, + }, + }, + }, + }); + + dataFilter.processEvent(event); + + expect(event.context['userId']).toBe(uuidWithManyDigits); + expect(event.context['sessionId']).toBe(uuidUpperCase); + expect(event.context['transactionId']).toBe(uuidNoDashes); + expect(event.addons['vue']['props']['componentId']).toBe(uuidWithManyDigits); + }); + + test('should not filter MongoDB ObjectId values that contain exactly 16 digits', async () => { + // These ObjectIds contain exactly 16 digits which when cleaned match PAN patterns + // Without ObjectId detection, they would be incorrectly filtered as credit cards + const objectIdWithManyDigits = '4111111111111111abcdefab'; // 16 digits + 8 hex letters = 24 chars, cleans to Visa pattern + const objectIdUpperCase = '5111111111111111ABCDEFAB'; // Cleans to Mastercard pattern + const objectIdMixedCase = '2111111111111111AbCdEfAb'; // Cleans to Maestro/Mastercard pattern + + const event = generateEvent({ + context: { + projectId: objectIdWithManyDigits, + workspaceId: objectIdUpperCase, + transactionId: objectIdMixedCase, + }, + addons: { + hawk: { + projectId: objectIdWithManyDigits, + }, + }, + }); + + dataFilter.processEvent(event); + + expect(event.context['projectId']).toBe(objectIdWithManyDigits); + expect(event.context['workspaceId']).toBe(objectIdUpperCase); + expect(event.context['transactionId']).toBe(objectIdMixedCase); + expect(event.addons['hawk']['projectId']).toBe(objectIdWithManyDigits); + }); + + test('should still filter actual PAN numbers with formatting characters', async () => { + // Test real Mastercard test number with spaces and dashes + const panWithSpaces = '5500 0000 0000 0004'; + const panWithDashes = '5500-0000-0000-0004'; + + const event = generateEvent({ + context: { + cardNumber: panWithSpaces, + paymentCard: panWithDashes, + }, + }); + + dataFilter.processEvent(event); + + expect(event.context['cardNumber']).toBe('[filtered]'); + expect(event.context['paymentCard']).toBe('[filtered]'); + }); + + test('should not filter values that are not UUIDs, ObjectIds, or PANs', async () => { + // These are edge cases that should NOT be filtered + const shortHex = '507f1f77bcf86cd7'; // 16 hex chars (not 24) + const longNumber = '67280841958304100309082499'; // 26 digits (too long for PAN) + const mixedAlphaNum = 'abc123def456ghi789'; // Mixed content + + const event = generateEvent({ + context: { + shortId: shortHex, + longId: longNumber, + mixedId: mixedAlphaNum, + }, + }); + + dataFilter.processEvent(event); + + expect(event.context['shortId']).toBe(shortHex); + expect(event.context['longId']).toBe(longNumber); + expect(event.context['mixedId']).toBe(mixedAlphaNum); + }); + + test('should filter UUIDs and ObjectIds when they are in sensitive key fields', async () => { + // Even if the value is a valid UUID or ObjectId, it should be filtered + // if the key name is in the sensitive keys list + const uuid = '550e8400-e29b-41d4-a716-446655440000'; + const objectId = '507f1f77bcf86cd799439011'; + + const event = generateEvent({ + context: { + password: uuid, + secret: objectId, + auth: '672808419583041003090824', + }, + }); + + dataFilter.processEvent(event); + + // All should be filtered because of sensitive key names + expect(event.context['password']).toBe('[filtered]'); + expect(event.context['secret']).toBe('[filtered]'); + expect(event.context['auth']).toBe('[filtered]'); + }); }); }); From c4cc417ab4fb9a148c54789920ee304267c39f42 Mon Sep 17 00:00:00 2001 From: Dobrunia Kostrigin <48620984+Dobrunia@users.noreply.github.com> Date: Mon, 2 Feb 2026 20:31:22 +0300 Subject: [PATCH 11/20] fix(grouper): filter oldPassword and newPassword in event payload (#516) * fix(grouper): filter oldPassword and newPassword in event payload * chore: add more keys and update tests * chore: lint fix * fix(tests): rename sessionId to requestId in data-filter tests for clarity --- workers/grouper/src/data-filter.ts | 70 +++++++++++++++++++--- workers/grouper/tests/data-filter.test.ts | 73 +++++++++++++++++++++-- 2 files changed, 132 insertions(+), 11 deletions(-) diff --git a/workers/grouper/src/data-filter.ts b/workers/grouper/src/data-filter.ts index 3571a1c6d..7e00038cd 100644 --- a/workers/grouper/src/data-filter.ts +++ b/workers/grouper/src/data-filter.ts @@ -36,17 +36,71 @@ export default class DataFilter { private filteredValuePlaceholder = '[filtered]'; /** - * Possibly sensitive keys + * Possibly sensitive keys (lowercase; keys are compared via key.toLowerCase()) */ private possiblySensitiveDataKeys = new Set([ - 'pan', - 'secret', - 'credentials', - 'card[number]', - 'password', + /** + * Authorization and sessions + */ 'auth', + 'authorization', 'access_token', 'accesstoken', + 'token', + 'jwt', + 'session', + 'sessionid', + 'session_id', + /** + * API keys and secure tokens + */ + 'api_key', + 'apikey', + 'x-api-key', + 'x-auth-token', + 'bearer', + 'client_secret', + 'secret', + 'credentials', + /** + * Passwords + */ + 'password', + 'passwd', + 'mysql_pwd', + 'oldpassword', + 'old-password', + 'old_password', + 'newpassword', + 'new-password', + 'new_password', + /** + * Encryption keys + */ + 'private_key', + 'ssh_key', + /** + * Payments data + */ + 'card', + 'cardnumber', + 'card[number]', + 'creditcard', + 'credit_card', + 'pan', + 'pin', + 'security_code', + 'stripetoken', + 'cloudpayments_public_id', + 'cloudpayments_secret', + /** + * Config and connections + */ + 'dsn', + /** + * Personal data + */ + 'ssn', ]); /** @@ -127,7 +181,9 @@ export default class DataFilter { */ const clean = value.replace(/\D/g, ''); - // Reset last index to 0 + /** + * Reset last index to 0 + */ this.bankCardRegex.lastIndex = 0; if (!this.bankCardRegex.test(clean)) { return value; diff --git a/workers/grouper/tests/data-filter.test.ts b/workers/grouper/tests/data-filter.test.ts index 28ff2979a..4cb988079 100644 --- a/workers/grouper/tests/data-filter.test.ts +++ b/workers/grouper/tests/data-filter.test.ts @@ -28,20 +28,57 @@ function generateEvent({ context, addons }: {context?: Json, addons?: EventAddon } /** - * Example of object with sensitive information + * Example of object with sensitive information. + * Keys intentionally use snake_case/kebab-case to match data-filter list. */ +/* eslint-disable @typescript-eslint/naming-convention */ const sensitiveDataMock = { pan: '5500 0000 0000 0004', secret: 'D6A03F5C2E0E356F262D56F44370E1CD813583B2', credentials: '70BA33708CBFB103F1A8E34AFEF333BA7DC021022B2D9AAA583AABB8058D8D67', 'card[number]': '5500 0000 0000 0004', password: 'bFb7PBm6nZ7RJRq9', + oldpassword: 'oldSecret123', + newpassword: 'newSecret456', + 'old-password': 'oldSecretHyphen', + old_password: 'oldSecretUnderscore', + 'new-password': 'newSecretHyphen', + new_password: 'newSecretUnderscore', auth: 'C4CA4238A0B923820DCC509A6F75849B', - // eslint-disable-next-line @typescript-eslint/naming-convention access_token: '70BA33708CBFB103F1A8E34AFEF333BA7DC021022B2D9AAA583AABB8058D8D67', accessToken: '70BA33708CBFB103F1A8E34AFEF333BA7DC021022B2D9AAA583AABB8058D8D67', }; +/** + * Additional sensitive keys (newly added / previously uncovered). + * Keys intentionally use snake_case to match data-filter list. + */ +const additionalSensitiveDataMock = { + authorization: 'Bearer abc123', + token: 'token-value', + jwt: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9', + session: 'sess_xyz', + session_id: 'sid_789', + api_key: 'sk_live_xxx', + bearer: 'Bearer token', + client_secret: 'client_secret_value', + passwd: 'passwd_value', + mysql_pwd: 'mysql_pwd_value', + private_key: '-----BEGIN PRIVATE KEY-----', + ssh_key: 'ssh-rsa AAAA...', + card: '4111111111111111', + cardnumber: '5500000000000004', + creditcard: '4111111111111111', + pin: '1234', + security_code: '999', + stripetoken: 'tok_xxx', + cloudpayments_public_id: 'pk_xxx', + cloudpayments_secret: 'secret_xxx', + dsn: 'postgres://user:pass@host/db', + ssn: '123-45-6789', +}; +/* eslint-enable @typescript-eslint/naming-convention */ + describe('GrouperWorker', () => { const dataFilter = new DataFilter(); @@ -123,6 +160,34 @@ describe('GrouperWorker', () => { }); }); + test('should filter additional sensitive keys (authorization, token, payment, dsn, ssn, etc.) in context', async () => { + const event = generateEvent({ + context: additionalSensitiveDataMock, + }); + + dataFilter.processEvent(event); + + Object.keys(additionalSensitiveDataMock).forEach((key) => { + expect(event.context[key]).toBe('[filtered]'); + }); + }); + + test('should filter additional sensitive keys in addons', async () => { + const event = generateEvent({ + addons: { + vue: { + props: additionalSensitiveDataMock, + }, + }, + }); + + dataFilter.processEvent(event); + + Object.keys(additionalSensitiveDataMock).forEach((key) => { + expect(event.addons['vue']['props'][key]).toBe('[filtered]'); + }); + }); + test('should not replace values with keynames not in a list', async () => { const normalValue = 'test123'; const event = generateEvent({ @@ -154,7 +219,7 @@ describe('GrouperWorker', () => { const event = generateEvent({ context: { userId: uuidWithManyDigits, - sessionId: uuidUpperCase, + requestId: uuidUpperCase, transactionId: uuidNoDashes, }, addons: { @@ -169,7 +234,7 @@ describe('GrouperWorker', () => { dataFilter.processEvent(event); expect(event.context['userId']).toBe(uuidWithManyDigits); - expect(event.context['sessionId']).toBe(uuidUpperCase); + expect(event.context['requestId']).toBe(uuidUpperCase); expect(event.context['transactionId']).toBe(uuidNoDashes); expect(event.addons['vue']['props']['componentId']).toBe(uuidWithManyDigits); }); From 8777e77c8706215d5fa0f890922d237cbed18417 Mon Sep 17 00:00:00 2001 From: e11sy <130844513+e11sy@users.noreply.github.com> Date: Sat, 31 Jan 2026 02:20:15 +0500 Subject: [PATCH 12/20] chore(grouper): add counters to the grouper worker --- workers/grouper/src/index.ts | 181 +++++++++++++++++ workers/grouper/src/redisHelper.ts | 213 ++++++++++++++++++- workers/grouper/tests/index.test.ts | 304 ++++++++++++++++++++++++++++ 3 files changed, 697 insertions(+), 1 deletion(-) diff --git a/workers/grouper/src/index.ts b/workers/grouper/src/index.ts index 73f16fc77..8a10b369c 100644 --- a/workers/grouper/src/index.ts +++ b/workers/grouper/src/index.ts @@ -26,6 +26,7 @@ import { rightTrim } from '../../../lib/utils/string'; import { hasValue } from '../../../lib/utils/hasValue'; /* eslint-disable-next-line no-unused-vars */ import { memoize } from '../../../lib/memoize'; +import TimeMs from '../../../lib/utils/time'; /** * eslint does not count decorators as a variable usage @@ -268,6 +269,186 @@ export default class GrouperWorker extends Worker { }); } } + + await this.incrementRateLimitCounter(task.projectId); + await this.recordProjectMetrics(task.projectId, 'events-stored'); + } + + /** + * Build RedisTimeSeries key for project metrics. + * + * @param projectId - id of the project + * @param metricType - metric type identifier + * @param granularity - time granularity + */ + private getTimeSeriesKey( + projectId: string, + metricType: string, + granularity: 'minutely' | 'hourly' | 'daily' + ): string { + return `ts:project-${metricType}:${projectId}:${granularity}`; + } + + /** + * Record project metrics to Redis TimeSeries. + * + * @param projectId - id of the project + * @param metricType - metric type identifier + */ + private async recordProjectMetrics(projectId: string, metricType: string): Promise { + const minutelyKey = this.getTimeSeriesKey(projectId, metricType, 'minutely'); + const hourlyKey = this.getTimeSeriesKey(projectId, metricType, 'hourly'); + const dailyKey = this.getTimeSeriesKey(projectId, metricType, 'daily'); + + const labels: Record = { + type: 'error', + status: metricType, + project: projectId, + }; + + const series = [ + { key: minutelyKey, label: 'minutely', retentionMs: TimeMs.DAY }, + { key: hourlyKey, label: 'hourly', retentionMs: TimeMs.WEEK }, + { key: dailyKey, label: 'daily', retentionMs: 90 * TimeMs.DAY }, + ]; + + for (const { key, label, retentionMs } of series) { + try { + await this.redis.safeTsAdd(key, 1, labels, retentionMs); + } catch (error) { + this.logger.error(`Failed to add ${label} TS for ${metricType}`, error); + } + } + } + + /** + * Increment rate limit counters for the project. + * + * @param projectId - id of the project + */ + private async incrementRateLimitCounter(projectId: string): Promise { + try { + const settings = await this.getProjectRateLimitSettings(projectId); + + if (!settings) { + return; + } + + await this.redis.incrementRateLimitCounterForCurrentEvent( + projectId, + settings.eventsPeriod, + settings.eventsLimit + ); + } catch (error) { + this.logger.error(`Failed to increment rate limit counter for project ${projectId}`, error); + } + } + + /** + * Fetch and normalize rate limit settings + * Rate limit settings could appear in tarifPlan, workspace and project. + * All rateLimits have different priority. + * + * @param projectId - id of the project + */ + @memoize({ max: 200, ttl: MEMOIZATION_TTL, strategy: 'concat', skipCache: [null] }) + private async getProjectRateLimitSettings(projectId: string): Promise<{ eventsLimit: number; eventsPeriod: number } | null> { + if (!projectId || !mongodb.ObjectID.isValid(projectId)) { + return null; + } + + const accountsDb = this.accountsDb.getConnection(); + + /** + * Fetch project from the db + */ + const project = await accountsDb + .collection('projects') + .findOne( + { _id: new mongodb.ObjectId(projectId) }, + { projection: { rateLimitSettings: 1, workspaceId: 1 } } + ); + + if (!project) { + return null; + } + + const projectRateLimitSettings = project.rateLimitSettings as { N: number, T: number }; + const workspaceId = new mongodb.ObjectID(project.workspaceId); + + let planRateLimitSettings: { N: number, T: number}; + let workspaceRateLimitSettings: { N: number, T: number}; + + /** + * Fetch workspace from the db + */ + if (workspaceId) { + const workspace = await accountsDb + .collection('workspaces') + .findOne( + { _id: workspaceId }, + { projection: { rateLimitSettings: 1, tariffPlanId: 1 } } + ); + + workspaceRateLimitSettings = workspace?.rateLimitSettings as { N: number, T: number }; + + const planId = new mongodb.ObjectId(workspace?.tariffPlanId); + + /** + * Tarif plan from the db + */ + if (planId) { + const plan = await accountsDb + .collection('plans') + .findOne( + { _id: planId }, + { projection: { rateLimitSettings: 1 } } + ); + + planRateLimitSettings = plan?.rateLimitSettings; + } + } + + return this.normalizeRateLimitSettings( + planRateLimitSettings, + workspaceRateLimitSettings, + projectRateLimitSettings + ); + } + + /** + * Normalize rate limit settings shape from database. + * + * @param rateLimitLayers - raw settings documents in priority order + */ + private normalizeRateLimitSettings( + ...rateLimitLayers: { N: number, T: number }[] + ): { eventsLimit: number; eventsPeriod: number } | null { + let eventsLimit = 0; + let eventsPeriod = 0; + + for (const layer of rateLimitLayers) { + if (!layer) { + continue; + } + + const limit = layer.N as number; + const period = layer.T as number; + + if (limit !== undefined && limit > 0) { + eventsLimit = limit; + } + + if (period !== undefined && period > 0) { + eventsPeriod = period; + } + } + + if (eventsLimit <= 0 || eventsPeriod <= 0) { + return null; + } + + return { eventsLimit, eventsPeriod }; } /** diff --git a/workers/grouper/src/redisHelper.ts b/workers/grouper/src/redisHelper.ts index a655c24bf..f0f45a7eb 100644 --- a/workers/grouper/src/redisHelper.ts +++ b/workers/grouper/src/redisHelper.ts @@ -110,6 +110,217 @@ export default class RedisHelper { return result === null; } + /** + * Increments redis counter used for rate limiting + * + * @param projectId - id of the project which event belongs to + * @param eventsPeriod - rate limit period configured for the project + */ + public async incrementRateLimitCounterForCurrentEvent(projectId: string, eventsPeriod: number, limit: number): Promise { + const script = ` + local key = KEYS[1] + local field = ARGV[1] + local now = tonumber(ARGV[2]) + local period = tonumber(ARGV[3]) + local limit = tonumber(ARGV[4]) + + local current = redis.call('HGET', key, field) + + -- If no record yet, start a new window with count = 1 + if not current then + redis.call('HSET', key, field, now .. ':1') + return + end + + local timestamp, count = string.match(current, '(%d+):(%d+)') + timestamp = tonumber(timestamp) + count = tonumber(count) + + -- Check if we're in a new time window + if now - timestamp >= period then + redis.call('HSET', key, field, now .. ':1') + return + end + + -- Check if incrementing would exceed limit + if count + 1 > limit then + return + end + + -- Increment counter + redis.call('HSET', key, field, timestamp .. ':' .. (count + 1)) + ` + + const key = 'rate_limits'; + const now = Math.floor(Date.now() / 1000); + + await this.redisClient.eval(script, { + keys: [key], + arguments: [projectId, now.toString(), eventsPeriod.toString(), limit.toString()], + }); + } + + /** + * Build label arguments for RedisTimeSeries commands + * + * @param labels - labels to attach to the time series + */ + private buildLabelArguments(labels: Record): string[] { + const labelArgs: string[] = ['LABELS']; + + for (const [labelKey, labelValue] of Object.entries(labels)) { + labelArgs.push(labelKey, labelValue); + } + + return labelArgs; + } + + /** + * Creates a RedisTimeSeries key if it doesn't exist. + * + * @param key - time series key + * @param labels - labels to attach to the time series + * @param retentionMs - optional retention in milliseconds + */ + public async tsCreateIfNotExists( + key: string, + labels: Record, + retentionMs = 0 + ): Promise { + const exists = await this.redisClient.exists(key); + + if (exists > 0) { + return; + } + + const args: string[] = ['TS.CREATE', key]; + + if (retentionMs > 0) { + args.push('RETENTION', Math.floor(retentionMs).toString()); + } + + args.push(...this.buildLabelArguments(labels)); + + await this.redisClient.sendCommand(args); + } + + /** + * Increments a RedisTimeSeries key with labels and timestamp. + * + * @param key - time series key + * @param value - value to increment by + * @param timestampMs - timestamp in milliseconds, defaults to current time + * @param labels - labels to attach to the sample + */ + public async tsIncrBy( + key: string, + value: number, + timestampMs = 0, + labels: Record = {} + ): Promise { + const labelArgs = this.buildLabelArguments(labels); + const timestamp = timestampMs === 0 ? Date.now() : timestampMs; + + const args: string[] = [ + 'TS.INCRBY', + key, + value.toString(), + 'TIMESTAMP', + Math.floor(timestamp).toString(), + ...labelArgs, + ]; + + await this.redisClient.sendCommand(args); + } + + /** + * Ensures that a RedisTimeSeries key exists and increments it safely. + * + * @param key - time series key + * @param value - value to increment by + * @param labels - labels to attach to the time series + * @param retentionMs - optional retention in milliseconds + */ + public async safeTsIncrBy( + key: string, + value: number, + labels: Record, + retentionMs = 0 + ): Promise { + const timestamp = Date.now(); + + try { + await this.tsIncrBy(key, value, timestamp, labels); + } catch (error) { + if (error instanceof Error && error.message.includes('TSDB: key does not exist')) { + this.logger.warn(`TS key ${key} does not exist, creating it...`); + await this.tsCreateIfNotExists(key, labels, retentionMs); + await this.tsIncrBy(key, value, timestamp, labels); + } else { + throw error; + } + } + } + + /** + * Adds a sample to a RedisTimeSeries key. + * + * @param key - time series key + * @param value - value to add + * @param timestampMs - timestamp in milliseconds, defaults to current time + * @param labels - labels to attach to the sample + */ + public async tsAdd( + key: string, + value: number, + timestampMs = 0, + labels: Record = {} + ): Promise { + const labelArgs = this.buildLabelArguments(labels); + const timestamp = timestampMs === 0 ? Date.now() : timestampMs; + + const args: string[] = [ + 'TS.ADD', + key, + Math.floor(timestamp).toString(), + value.toString(), + 'ON_DUPLICATE', + 'SUM', + ...labelArgs, + ]; + + await this.redisClient.sendCommand(args); + } + + /** + * Ensures that a RedisTimeSeries key exists and adds a sample safely. + * + * @param key - time series key + * @param value - value to add + * @param labels - labels to attach to the time series + * @param retentionMs - optional retention in milliseconds + */ + public async safeTsAdd( + key: string, + value: number, + labels: Record, + retentionMs = 0 + ): Promise { + const timestamp = Date.now(); + + try { + await this.tsAdd(key, value, timestamp, labels); + } catch (error) { + if (error instanceof Error && error.message.includes('TSDB: key does not exist')) { + this.logger.warn(`TS key ${key} does not exist, creating it...`); + await this.tsCreateIfNotExists(key, labels, retentionMs); + await this.tsAdd(key, value, timestamp, labels); + } else { + throw error; + } + } + } + /** * Creates callback function for Redis operations * @@ -130,4 +341,4 @@ export default class RedisHelper { resolve(resp !== 'OK'); }; } -} +} \ No newline at end of file diff --git a/workers/grouper/tests/index.test.ts b/workers/grouper/tests/index.test.ts index ee781e98a..2ae618101 100644 --- a/workers/grouper/tests/index.test.ts +++ b/workers/grouper/tests/index.test.ts @@ -7,6 +7,7 @@ import type { Collection } from 'mongodb'; import { MongoClient } from 'mongodb'; import type { ErrorsCatcherType, EventAddons, EventData } from '@hawk.so/types'; import { MS_IN_SEC } from '../../../lib/utils/consts'; +import TimeMs from '../../../lib/utils/time'; import * as mongodb from 'mongodb'; import { patch } from '@n1ru4l/json-patch-plus'; @@ -57,16 +58,41 @@ const projectIdMock = '5d206f7f9aaf7c0071d64596'; /** * Mock project data */ +const planIdMock = new mongodb.ObjectId(); +const workspaceIdMock = new mongodb.ObjectId(); + +const planMock = { + _id: planIdMock, + rateLimitSettings: { + N: 0, + T: 0, + }, +}; + +const workspaceMock = { + _id: workspaceIdMock, + tariffPlanId: planIdMock, + rateLimitSettings: { + N: 0, + T: 0, + }, +}; + const projectMock = { _id: new mongodb.ObjectId(projectIdMock), id: projectIdMock, name: 'Test Project', token: 'test-token', + workspaceId: workspaceIdMock, uidAdded: { id: 'test-user-id', }, unreadCount: 0, description: 'Test project for grouper worker tests', + rateLimitSettings: { + N: 0, + T: 0, + }, eventGroupingPatterns: [ { _id: mongodb.ObjectId(), pattern: 'New error .*', @@ -113,8 +139,30 @@ describe('GrouperWorker', () => { let dailyEventsCollection: Collection; let repetitionsCollection: Collection; let projectsCollection: Collection; + let workspacesCollection: Collection; + let plansCollection: Collection; let redisClient: RedisClientType; let worker: GrouperWorker; + const setPlanRateLimit = async (eventsLimit: number, eventsPeriod: number): Promise => { + await plansCollection.updateOne( + { _id: planIdMock }, + { $set: { rateLimitSettings: { N: eventsLimit, T: eventsPeriod } } }, + { upsert: true } + ); + }; + const setWorkspaceRateLimit = async (eventsLimit: number, eventsPeriod: number): Promise => { + await workspacesCollection.updateOne( + { _id: workspaceIdMock }, + { $set: { rateLimitSettings: { N: eventsLimit, T: eventsPeriod } } }, + { upsert: true } + ); + }; + const setProjectRateLimit = async (eventsLimit: number, eventsPeriod: number): Promise => { + await projectsCollection.updateOne( + { _id: new mongodb.ObjectId(projectIdMock) }, + { $set: { rateLimitSettings: { N: eventsLimit, T: eventsPeriod } } }, + ); + }; beforeAll(async () => { worker = new GrouperWorker(); @@ -133,12 +181,17 @@ describe('GrouperWorker', () => { dailyEventsCollection = connection.db().collection('dailyEvents:' + projectIdMock); repetitionsCollection = connection.db().collection('repetitions:' + projectIdMock); projectsCollection = accountsConnection.db().collection('projects'); + workspacesCollection = accountsConnection.db().collection('workspaces'); + plansCollection = accountsConnection.db().collection('plans'); /** * Create unique index for groupHash */ await eventsCollection.createIndex({ groupHash: 1 }, { unique: true }); + await plansCollection.insertOne(planMock); + await workspacesCollection.insertOne(workspaceMock); + /** * Insert mock project into accounts database */ @@ -155,9 +208,13 @@ describe('GrouperWorker', () => { */ beforeEach(async () => { worker.clearCache(); + delete (worker as any)['memoizeCache:getProjectRateLimitSettings']; await eventsCollection.deleteMany({}); await dailyEventsCollection.deleteMany({}); await repetitionsCollection.deleteMany({}); + await setPlanRateLimit(0, 0); + await setWorkspaceRateLimit(0, 0); + await setProjectRateLimit(0, 0); }); afterEach(async () => { @@ -743,10 +800,257 @@ describe('GrouperWorker', () => { }); }); + describe('Rate limits counter increment', () => { + const rateLimitsKey = 'rate_limits'; + + test('increments counter when handling an event', async () => { + await setProjectRateLimit(5, 60); + + let currentTime = 1_000_000; + const nowSpy = jest.spyOn(Date, 'now').mockImplementation(() => currentTime); + + try { + await worker.handle(generateTask()); + + const storedValue = await redisClient.hGet(rateLimitsKey, projectIdMock); + + expect(storedValue).toBe(`${Math.floor(currentTime / 1000)}:1`); + } finally { + nowSpy.mockRestore(); + } + }); + + test('reuses window and increments while within limit', async () => { + await setProjectRateLimit(5, 60); + + let currentTime = 2_000_000; + const nowSpy = jest.spyOn(Date, 'now').mockImplementation(() => currentTime); + + try { + await worker.handle(generateTask()); + await worker.handle(generateTask()); + + const storedValue = await redisClient.hGet(rateLimitsKey, projectIdMock); + + expect(storedValue).not.toBeNull(); + + const [, count] = (storedValue as string).split(':'); + + expect(Number(count)).toBe(2); + } finally { + nowSpy.mockRestore(); + } + }); + + test('does not exceed configured limit within same window', async () => { + const eventsLimit = 3; + + await setProjectRateLimit(eventsLimit, 60); + + let currentTime = 3_000_000; + const nowSpy = jest.spyOn(Date, 'now').mockImplementation(() => currentTime); + + try { + for (let i = 0; i < eventsLimit + 2; i++) { + await worker.handle(generateTask()); + } + + const storedValue = await redisClient.hGet(rateLimitsKey, projectIdMock); + + expect(storedValue).not.toBeNull(); + + const [, count] = (storedValue as string).split(':'); + + expect(Number(count)).toBe(eventsLimit); + } finally { + nowSpy.mockRestore(); + } + }); + + test('resets window after period elapses', async () => { + await setProjectRateLimit(5, 2); + + let currentTime = 4_000_000; + const nowSpy = jest.spyOn(Date, 'now').mockImplementation(() => currentTime); + + try { + await worker.handle(generateTask()); + + currentTime += 3_000; // advance by 3 seconds + + await worker.handle(generateTask()); + + const storedValue = await redisClient.hGet(rateLimitsKey, projectIdMock); + + expect(storedValue).not.toBeNull(); + + const [timestamp, count] = (storedValue as string).split(':'); + + expect(Number(timestamp)).toBe(Math.floor(currentTime / 1000)); + expect(Number(count)).toBe(1); + } finally { + nowSpy.mockRestore(); + } + }); + + test('uses workspace limits when project overrides are absent', async () => { + await setPlanRateLimit(10, 60); + await setWorkspaceRateLimit(3, 60); + await setProjectRateLimit(0, 0); + + let currentTime = 5_000_000; + const nowSpy = jest.spyOn(Date, 'now').mockImplementation(() => currentTime); + + try { + for (let i = 0; i < 5; i++) { + await worker.handle(generateTask()); + } + + const storedValue = await redisClient.hGet(rateLimitsKey, projectIdMock); + + expect(storedValue).not.toBeNull(); + + const [, count] = (storedValue as string).split(':'); + + expect(Number(count)).toBe(3); + } finally { + nowSpy.mockRestore(); + } + }); + + test('falls back to plan limits when workspace settings are empty', async () => { + await setPlanRateLimit(4, 60); + await setWorkspaceRateLimit(0, 0); + await setProjectRateLimit(0, 0); + + let currentTime = 6_000_000; + const nowSpy = jest.spyOn(Date, 'now').mockImplementation(() => currentTime); + + try { + for (let i = 0; i < 6; i++) { + await worker.handle(generateTask()); + } + + const storedValue = await redisClient.hGet(rateLimitsKey, projectIdMock); + + expect(storedValue).not.toBeNull(); + + const [, count] = (storedValue as string).split(':'); + + expect(Number(count)).toBe(4); + } finally { + nowSpy.mockRestore(); + } + }); + + test('prefers project limits over workspace and plan', async () => { + await setPlanRateLimit(4, 60); + await setWorkspaceRateLimit(6, 60); + await setProjectRateLimit(8, 60); + + let currentTime = 7_000_000; + const nowSpy = jest.spyOn(Date, 'now').mockImplementation(() => currentTime); + + try { + for (let i = 0; i < 10; i++) { + await worker.handle(generateTask()); + } + + const storedValue = await redisClient.hGet(rateLimitsKey, projectIdMock); + + expect(storedValue).not.toBeNull(); + + const [, count] = (storedValue as string).split(':'); + + expect(Number(count)).toBe(8); + } finally { + nowSpy.mockRestore(); + } + }); + }); + + describe('Events-stored metrics', () => { + test('writes minutely, hourly, and daily samples after handling an event', async () => { + const safeTsAddSpy = jest.spyOn((worker as any).redis, 'safeTsAdd'); + + try { + await worker.handle(generateTask()); + + expect(safeTsAddSpy).toHaveBeenCalledTimes(3); + + const expectedLabels = { + type: 'error', + status: 'events-stored', + project: projectIdMock, + }; + + expect(safeTsAddSpy).toHaveBeenNthCalledWith( + 1, + `ts:project-events-stored:${projectIdMock}:minutely`, + 1, + expectedLabels, + TimeMs.DAY, + ); + expect(safeTsAddSpy).toHaveBeenNthCalledWith( + 2, + `ts:project-events-stored:${projectIdMock}:hourly`, + 1, + expectedLabels, + TimeMs.WEEK, + ); + expect(safeTsAddSpy).toHaveBeenNthCalledWith( + 3, + `ts:project-events-stored:${projectIdMock}:daily`, + 1, + expectedLabels, + 90 * TimeMs.DAY, + ); + } finally { + safeTsAddSpy.mockRestore(); + } + }); + + test('logs when a time-series write fails but continues processing', async () => { + const safeTsAddSpy = jest.spyOn((worker as any).redis, 'safeTsAdd'); + const loggerErrorSpy = jest.spyOn((worker as any).logger, 'error').mockImplementation(() => undefined); + const failure = new Error('TS failure'); + + safeTsAddSpy + .mockImplementationOnce(() => Promise.resolve()) + .mockImplementationOnce(async () => { throw failure; }) + .mockImplementationOnce(() => Promise.resolve()); + + try { + await worker.handle(generateTask()); + + expect(loggerErrorSpy).toHaveBeenCalledWith('Failed to add hourly TS for events-stored', failure); + expect(await eventsCollection.find().count()).toBe(1); + } finally { + safeTsAddSpy.mockRestore(); + loggerErrorSpy.mockRestore(); + } + }); + + test('records metrics exactly once per handled event', async () => { + const recordMetricsSpy = jest.spyOn(worker as any, 'recordProjectMetrics'); + + try { + await worker.handle(generateTask()); + + expect(recordMetricsSpy).toHaveBeenCalledTimes(1); + expect(recordMetricsSpy).toHaveBeenCalledWith(projectIdMock, 'events-stored'); + } finally { + recordMetricsSpy.mockRestore(); + } + }); + }); + afterAll(async () => { await redisClient.quit(); await worker.finish(); await projectsCollection.deleteMany({}); + await workspacesCollection.deleteMany({}); + await plansCollection.deleteMany({}); await accountsConnection.close(); await connection.close(); }); From 3badfa93c4ced6f085356c2d114fbef707fecee7 Mon Sep 17 00:00:00 2001 From: e11sy <130844513+e11sy@users.noreply.github.com> Date: Sat, 7 Feb 2026 17:49:23 +0300 Subject: [PATCH 13/20] chore(): eslint fix --- workers/grouper/src/index.ts | 1 + workers/grouper/src/redisHelper.ts | 33 +++++++++++++++--------------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/workers/grouper/src/index.ts b/workers/grouper/src/index.ts index 8a10b369c..f42789ed7 100644 --- a/workers/grouper/src/index.ts +++ b/workers/grouper/src/index.ts @@ -309,6 +309,7 @@ export default class GrouperWorker extends Worker { const series = [ { key: minutelyKey, label: 'minutely', retentionMs: TimeMs.DAY }, { key: hourlyKey, label: 'hourly', retentionMs: TimeMs.WEEK }, + /* eslint-disable-next-line @typescript-eslint/no-magic-numbers */ { key: dailyKey, label: 'daily', retentionMs: 90 * TimeMs.DAY }, ]; diff --git a/workers/grouper/src/redisHelper.ts b/workers/grouper/src/redisHelper.ts index f0f45a7eb..0d2072087 100644 --- a/workers/grouper/src/redisHelper.ts +++ b/workers/grouper/src/redisHelper.ts @@ -2,6 +2,7 @@ import HawkCatcher from '@hawk.so/nodejs'; import type { RedisClientType } from 'redis'; import { createClient } from 'redis'; import createLogger from '../../../lib/logger'; +import { MS_IN_SEC } from '../../../lib/utils/consts'; /** * Class with helper functions for working with Redis @@ -152,7 +153,7 @@ export default class RedisHelper { ` const key = 'rate_limits'; - const now = Math.floor(Date.now() / 1000); + const now = Math.floor(Date.now() / MS_IN_SEC); await this.redisClient.eval(script, { keys: [key], @@ -160,21 +161,6 @@ export default class RedisHelper { }); } - /** - * Build label arguments for RedisTimeSeries commands - * - * @param labels - labels to attach to the time series - */ - private buildLabelArguments(labels: Record): string[] { - const labelArgs: string[] = ['LABELS']; - - for (const [labelKey, labelValue] of Object.entries(labels)) { - labelArgs.push(labelKey, labelValue); - } - - return labelArgs; - } - /** * Creates a RedisTimeSeries key if it doesn't exist. * @@ -321,6 +307,21 @@ export default class RedisHelper { } } + /** + * Build label arguments for RedisTimeSeries commands + * + * @param labels - labels to attach to the time series + */ + private buildLabelArguments(labels: Record): string[] { + const labelArgs: string[] = ['LABELS']; + + for (const [labelKey, labelValue] of Object.entries(labels)) { + labelArgs.push(labelKey, labelValue); + } + + return labelArgs; + } + /** * Creates callback function for Redis operations * From 0d00df96b33ac3ac2a5eb62a65575861f832ff86 Mon Sep 17 00:00:00 2001 From: e11sy <130844513+e11sy@users.noreply.github.com> Date: Sat, 7 Feb 2026 17:59:33 +0300 Subject: [PATCH 14/20] chore(): clean up --- workers/grouper/src/index.ts | 39 ++++++++++++++++++----- workers/grouper/src/redisHelper.ts | 7 +++-- workers/grouper/tests/index.test.ts | 49 +++++++++++++++++++++-------- 3 files changed, 72 insertions(+), 23 deletions(-) diff --git a/workers/grouper/src/index.ts b/workers/grouper/src/index.ts index f42789ed7..52b9c705c 100644 --- a/workers/grouper/src/index.ts +++ b/workers/grouper/src/index.ts @@ -307,10 +307,22 @@ export default class GrouperWorker extends Worker { }; const series = [ - { key: minutelyKey, label: 'minutely', retentionMs: TimeMs.DAY }, - { key: hourlyKey, label: 'hourly', retentionMs: TimeMs.WEEK }, - /* eslint-disable-next-line @typescript-eslint/no-magic-numbers */ - { key: dailyKey, label: 'daily', retentionMs: 90 * TimeMs.DAY }, + { + key: minutelyKey, + label: 'minutely', + retentionMs: TimeMs.DAY, + }, + { + key: hourlyKey, + label: 'hourly', + retentionMs: TimeMs.WEEK, + }, + { + key: dailyKey, + label: 'daily', + // eslint-disable-next-line @typescript-eslint/no-magic-numbers + retentionMs: 90 * TimeMs.DAY, + }, ]; for (const { key, label, retentionMs } of series) { @@ -367,7 +379,12 @@ export default class GrouperWorker extends Worker { .collection('projects') .findOne( { _id: new mongodb.ObjectId(projectId) }, - { projection: { rateLimitSettings: 1, workspaceId: 1 } } + { + projection: { + rateLimitSettings: 1, + workspaceId: 1, + }, + } ); if (!project) { @@ -388,7 +405,12 @@ export default class GrouperWorker extends Worker { .collection('workspaces') .findOne( { _id: workspaceId }, - { projection: { rateLimitSettings: 1, tariffPlanId: 1 } } + { + projection: { + rateLimitSettings: 1, + tariffPlanId: 1, + }, + } ); workspaceRateLimitSettings = workspace?.rateLimitSettings as { N: number, T: number }; @@ -449,7 +471,10 @@ export default class GrouperWorker extends Worker { return null; } - return { eventsLimit, eventsPeriod }; + return { + eventsLimit, + eventsPeriod, + }; } /** diff --git a/workers/grouper/src/redisHelper.ts b/workers/grouper/src/redisHelper.ts index 0d2072087..bed08418e 100644 --- a/workers/grouper/src/redisHelper.ts +++ b/workers/grouper/src/redisHelper.ts @@ -116,6 +116,7 @@ export default class RedisHelper { * * @param projectId - id of the project which event belongs to * @param eventsPeriod - rate limit period configured for the project + * @param limit - current event count limit (from project / workspace / plan) */ public async incrementRateLimitCounterForCurrentEvent(projectId: string, eventsPeriod: number, limit: number): Promise { const script = ` @@ -150,13 +151,13 @@ export default class RedisHelper { -- Increment counter redis.call('HSET', key, field, timestamp .. ':' .. (count + 1)) - ` + `; const key = 'rate_limits'; const now = Math.floor(Date.now() / MS_IN_SEC); await this.redisClient.eval(script, { - keys: [key], + keys: [ key ], arguments: [projectId, now.toString(), eventsPeriod.toString(), limit.toString()], }); } @@ -313,7 +314,7 @@ export default class RedisHelper { * @param labels - labels to attach to the time series */ private buildLabelArguments(labels: Record): string[] { - const labelArgs: string[] = ['LABELS']; + const labelArgs: string[] = [ 'LABELS' ]; for (const [labelKey, labelValue] of Object.entries(labels)) { labelArgs.push(labelKey, labelValue); diff --git a/workers/grouper/tests/index.test.ts b/workers/grouper/tests/index.test.ts index 2ae618101..34bad1ccf 100644 --- a/workers/grouper/tests/index.test.ts +++ b/workers/grouper/tests/index.test.ts @@ -146,21 +146,42 @@ describe('GrouperWorker', () => { const setPlanRateLimit = async (eventsLimit: number, eventsPeriod: number): Promise => { await plansCollection.updateOne( { _id: planIdMock }, - { $set: { rateLimitSettings: { N: eventsLimit, T: eventsPeriod } } }, + { + $set: { + rateLimitSettings: { + N: eventsLimit, + T: eventsPeriod, + }, + }, + }, { upsert: true } ); }; const setWorkspaceRateLimit = async (eventsLimit: number, eventsPeriod: number): Promise => { await workspacesCollection.updateOne( { _id: workspaceIdMock }, - { $set: { rateLimitSettings: { N: eventsLimit, T: eventsPeriod } } }, + { + $set: { + rateLimitSettings: { + N: eventsLimit, + T: eventsPeriod, + }, + }, + }, { upsert: true } ); }; const setProjectRateLimit = async (eventsLimit: number, eventsPeriod: number): Promise => { await projectsCollection.updateOne( { _id: new mongodb.ObjectId(projectIdMock) }, - { $set: { rateLimitSettings: { N: eventsLimit, T: eventsPeriod } } }, + { + $set: { + rateLimitSettings: { + N: eventsLimit, + T: eventsPeriod, + }, + }, + } ); }; @@ -806,7 +827,7 @@ describe('GrouperWorker', () => { test('increments counter when handling an event', async () => { await setProjectRateLimit(5, 60); - let currentTime = 1_000_000; + const currentTime = 1_000_000; const nowSpy = jest.spyOn(Date, 'now').mockImplementation(() => currentTime); try { @@ -823,7 +844,7 @@ describe('GrouperWorker', () => { test('reuses window and increments while within limit', async () => { await setProjectRateLimit(5, 60); - let currentTime = 2_000_000; + const currentTime = 2_000_000; const nowSpy = jest.spyOn(Date, 'now').mockImplementation(() => currentTime); try { @@ -847,7 +868,7 @@ describe('GrouperWorker', () => { await setProjectRateLimit(eventsLimit, 60); - let currentTime = 3_000_000; + const currentTime = 3_000_000; const nowSpy = jest.spyOn(Date, 'now').mockImplementation(() => currentTime); try { @@ -898,7 +919,7 @@ describe('GrouperWorker', () => { await setWorkspaceRateLimit(3, 60); await setProjectRateLimit(0, 0); - let currentTime = 5_000_000; + const currentTime = 5_000_000; const nowSpy = jest.spyOn(Date, 'now').mockImplementation(() => currentTime); try { @@ -923,7 +944,7 @@ describe('GrouperWorker', () => { await setWorkspaceRateLimit(0, 0); await setProjectRateLimit(0, 0); - let currentTime = 6_000_000; + const currentTime = 6_000_000; const nowSpy = jest.spyOn(Date, 'now').mockImplementation(() => currentTime); try { @@ -948,7 +969,7 @@ describe('GrouperWorker', () => { await setWorkspaceRateLimit(6, 60); await setProjectRateLimit(8, 60); - let currentTime = 7_000_000; + const currentTime = 7_000_000; const nowSpy = jest.spyOn(Date, 'now').mockImplementation(() => currentTime); try { @@ -989,21 +1010,21 @@ describe('GrouperWorker', () => { `ts:project-events-stored:${projectIdMock}:minutely`, 1, expectedLabels, - TimeMs.DAY, + TimeMs.DAY ); expect(safeTsAddSpy).toHaveBeenNthCalledWith( 2, `ts:project-events-stored:${projectIdMock}:hourly`, 1, expectedLabels, - TimeMs.WEEK, + TimeMs.WEEK ); expect(safeTsAddSpy).toHaveBeenNthCalledWith( 3, `ts:project-events-stored:${projectIdMock}:daily`, 1, expectedLabels, - 90 * TimeMs.DAY, + 90 * TimeMs.DAY ); } finally { safeTsAddSpy.mockRestore(); @@ -1017,7 +1038,9 @@ describe('GrouperWorker', () => { safeTsAddSpy .mockImplementationOnce(() => Promise.resolve()) - .mockImplementationOnce(async () => { throw failure; }) + .mockImplementationOnce(async () => { + throw failure; + }) .mockImplementationOnce(() => Promise.resolve()); try { From 20990d34f56a3846ff0a22f0c48845484aaea605 Mon Sep 17 00:00:00 2001 From: e11sy <130844513+e11sy@users.noreply.github.com> Date: Sun, 8 Feb 2026 20:05:48 +0300 Subject: [PATCH 15/20] chore(grouper): remove redundant rate-limit increment logic --- workers/grouper/src/index.ts | 144 ------------------- workers/grouper/src/redisHelper.ts | 51 ------- workers/grouper/tests/index.test.ts | 215 ---------------------------- 3 files changed, 410 deletions(-) diff --git a/workers/grouper/src/index.ts b/workers/grouper/src/index.ts index 52b9c705c..05aff09b0 100644 --- a/workers/grouper/src/index.ts +++ b/workers/grouper/src/index.ts @@ -270,7 +270,6 @@ export default class GrouperWorker extends Worker { } } - await this.incrementRateLimitCounter(task.projectId); await this.recordProjectMetrics(task.projectId, 'events-stored'); } @@ -334,149 +333,6 @@ export default class GrouperWorker extends Worker { } } - /** - * Increment rate limit counters for the project. - * - * @param projectId - id of the project - */ - private async incrementRateLimitCounter(projectId: string): Promise { - try { - const settings = await this.getProjectRateLimitSettings(projectId); - - if (!settings) { - return; - } - - await this.redis.incrementRateLimitCounterForCurrentEvent( - projectId, - settings.eventsPeriod, - settings.eventsLimit - ); - } catch (error) { - this.logger.error(`Failed to increment rate limit counter for project ${projectId}`, error); - } - } - - /** - * Fetch and normalize rate limit settings - * Rate limit settings could appear in tarifPlan, workspace and project. - * All rateLimits have different priority. - * - * @param projectId - id of the project - */ - @memoize({ max: 200, ttl: MEMOIZATION_TTL, strategy: 'concat', skipCache: [null] }) - private async getProjectRateLimitSettings(projectId: string): Promise<{ eventsLimit: number; eventsPeriod: number } | null> { - if (!projectId || !mongodb.ObjectID.isValid(projectId)) { - return null; - } - - const accountsDb = this.accountsDb.getConnection(); - - /** - * Fetch project from the db - */ - const project = await accountsDb - .collection('projects') - .findOne( - { _id: new mongodb.ObjectId(projectId) }, - { - projection: { - rateLimitSettings: 1, - workspaceId: 1, - }, - } - ); - - if (!project) { - return null; - } - - const projectRateLimitSettings = project.rateLimitSettings as { N: number, T: number }; - const workspaceId = new mongodb.ObjectID(project.workspaceId); - - let planRateLimitSettings: { N: number, T: number}; - let workspaceRateLimitSettings: { N: number, T: number}; - - /** - * Fetch workspace from the db - */ - if (workspaceId) { - const workspace = await accountsDb - .collection('workspaces') - .findOne( - { _id: workspaceId }, - { - projection: { - rateLimitSettings: 1, - tariffPlanId: 1, - }, - } - ); - - workspaceRateLimitSettings = workspace?.rateLimitSettings as { N: number, T: number }; - - const planId = new mongodb.ObjectId(workspace?.tariffPlanId); - - /** - * Tarif plan from the db - */ - if (planId) { - const plan = await accountsDb - .collection('plans') - .findOne( - { _id: planId }, - { projection: { rateLimitSettings: 1 } } - ); - - planRateLimitSettings = plan?.rateLimitSettings; - } - } - - return this.normalizeRateLimitSettings( - planRateLimitSettings, - workspaceRateLimitSettings, - projectRateLimitSettings - ); - } - - /** - * Normalize rate limit settings shape from database. - * - * @param rateLimitLayers - raw settings documents in priority order - */ - private normalizeRateLimitSettings( - ...rateLimitLayers: { N: number, T: number }[] - ): { eventsLimit: number; eventsPeriod: number } | null { - let eventsLimit = 0; - let eventsPeriod = 0; - - for (const layer of rateLimitLayers) { - if (!layer) { - continue; - } - - const limit = layer.N as number; - const period = layer.T as number; - - if (limit !== undefined && limit > 0) { - eventsLimit = limit; - } - - if (period !== undefined && period > 0) { - eventsPeriod = period; - } - } - - if (eventsLimit <= 0 || eventsPeriod <= 0) { - return null; - } - - return { - eventsLimit, - eventsPeriod, - }; - } - /** * Trims source code lines in event's backtrace to prevent memory leaks * diff --git a/workers/grouper/src/redisHelper.ts b/workers/grouper/src/redisHelper.ts index bed08418e..cc009cd59 100644 --- a/workers/grouper/src/redisHelper.ts +++ b/workers/grouper/src/redisHelper.ts @@ -111,57 +111,6 @@ export default class RedisHelper { return result === null; } - /** - * Increments redis counter used for rate limiting - * - * @param projectId - id of the project which event belongs to - * @param eventsPeriod - rate limit period configured for the project - * @param limit - current event count limit (from project / workspace / plan) - */ - public async incrementRateLimitCounterForCurrentEvent(projectId: string, eventsPeriod: number, limit: number): Promise { - const script = ` - local key = KEYS[1] - local field = ARGV[1] - local now = tonumber(ARGV[2]) - local period = tonumber(ARGV[3]) - local limit = tonumber(ARGV[4]) - - local current = redis.call('HGET', key, field) - - -- If no record yet, start a new window with count = 1 - if not current then - redis.call('HSET', key, field, now .. ':1') - return - end - - local timestamp, count = string.match(current, '(%d+):(%d+)') - timestamp = tonumber(timestamp) - count = tonumber(count) - - -- Check if we're in a new time window - if now - timestamp >= period then - redis.call('HSET', key, field, now .. ':1') - return - end - - -- Check if incrementing would exceed limit - if count + 1 > limit then - return - end - - -- Increment counter - redis.call('HSET', key, field, timestamp .. ':' .. (count + 1)) - `; - - const key = 'rate_limits'; - const now = Math.floor(Date.now() / MS_IN_SEC); - - await this.redisClient.eval(script, { - keys: [ key ], - arguments: [projectId, now.toString(), eventsPeriod.toString(), limit.toString()], - }); - } - /** * Creates a RedisTimeSeries key if it doesn't exist. * diff --git a/workers/grouper/tests/index.test.ts b/workers/grouper/tests/index.test.ts index 34bad1ccf..2986bf7fd 100644 --- a/workers/grouper/tests/index.test.ts +++ b/workers/grouper/tests/index.test.ts @@ -143,48 +143,6 @@ describe('GrouperWorker', () => { let plansCollection: Collection; let redisClient: RedisClientType; let worker: GrouperWorker; - const setPlanRateLimit = async (eventsLimit: number, eventsPeriod: number): Promise => { - await plansCollection.updateOne( - { _id: planIdMock }, - { - $set: { - rateLimitSettings: { - N: eventsLimit, - T: eventsPeriod, - }, - }, - }, - { upsert: true } - ); - }; - const setWorkspaceRateLimit = async (eventsLimit: number, eventsPeriod: number): Promise => { - await workspacesCollection.updateOne( - { _id: workspaceIdMock }, - { - $set: { - rateLimitSettings: { - N: eventsLimit, - T: eventsPeriod, - }, - }, - }, - { upsert: true } - ); - }; - const setProjectRateLimit = async (eventsLimit: number, eventsPeriod: number): Promise => { - await projectsCollection.updateOne( - { _id: new mongodb.ObjectId(projectIdMock) }, - { - $set: { - rateLimitSettings: { - N: eventsLimit, - T: eventsPeriod, - }, - }, - } - ); - }; - beforeAll(async () => { worker = new GrouperWorker(); @@ -229,13 +187,9 @@ describe('GrouperWorker', () => { */ beforeEach(async () => { worker.clearCache(); - delete (worker as any)['memoizeCache:getProjectRateLimitSettings']; await eventsCollection.deleteMany({}); await dailyEventsCollection.deleteMany({}); await repetitionsCollection.deleteMany({}); - await setPlanRateLimit(0, 0); - await setWorkspaceRateLimit(0, 0); - await setProjectRateLimit(0, 0); }); afterEach(async () => { @@ -821,175 +775,6 @@ describe('GrouperWorker', () => { }); }); - describe('Rate limits counter increment', () => { - const rateLimitsKey = 'rate_limits'; - - test('increments counter when handling an event', async () => { - await setProjectRateLimit(5, 60); - - const currentTime = 1_000_000; - const nowSpy = jest.spyOn(Date, 'now').mockImplementation(() => currentTime); - - try { - await worker.handle(generateTask()); - - const storedValue = await redisClient.hGet(rateLimitsKey, projectIdMock); - - expect(storedValue).toBe(`${Math.floor(currentTime / 1000)}:1`); - } finally { - nowSpy.mockRestore(); - } - }); - - test('reuses window and increments while within limit', async () => { - await setProjectRateLimit(5, 60); - - const currentTime = 2_000_000; - const nowSpy = jest.spyOn(Date, 'now').mockImplementation(() => currentTime); - - try { - await worker.handle(generateTask()); - await worker.handle(generateTask()); - - const storedValue = await redisClient.hGet(rateLimitsKey, projectIdMock); - - expect(storedValue).not.toBeNull(); - - const [, count] = (storedValue as string).split(':'); - - expect(Number(count)).toBe(2); - } finally { - nowSpy.mockRestore(); - } - }); - - test('does not exceed configured limit within same window', async () => { - const eventsLimit = 3; - - await setProjectRateLimit(eventsLimit, 60); - - const currentTime = 3_000_000; - const nowSpy = jest.spyOn(Date, 'now').mockImplementation(() => currentTime); - - try { - for (let i = 0; i < eventsLimit + 2; i++) { - await worker.handle(generateTask()); - } - - const storedValue = await redisClient.hGet(rateLimitsKey, projectIdMock); - - expect(storedValue).not.toBeNull(); - - const [, count] = (storedValue as string).split(':'); - - expect(Number(count)).toBe(eventsLimit); - } finally { - nowSpy.mockRestore(); - } - }); - - test('resets window after period elapses', async () => { - await setProjectRateLimit(5, 2); - - let currentTime = 4_000_000; - const nowSpy = jest.spyOn(Date, 'now').mockImplementation(() => currentTime); - - try { - await worker.handle(generateTask()); - - currentTime += 3_000; // advance by 3 seconds - - await worker.handle(generateTask()); - - const storedValue = await redisClient.hGet(rateLimitsKey, projectIdMock); - - expect(storedValue).not.toBeNull(); - - const [timestamp, count] = (storedValue as string).split(':'); - - expect(Number(timestamp)).toBe(Math.floor(currentTime / 1000)); - expect(Number(count)).toBe(1); - } finally { - nowSpy.mockRestore(); - } - }); - - test('uses workspace limits when project overrides are absent', async () => { - await setPlanRateLimit(10, 60); - await setWorkspaceRateLimit(3, 60); - await setProjectRateLimit(0, 0); - - const currentTime = 5_000_000; - const nowSpy = jest.spyOn(Date, 'now').mockImplementation(() => currentTime); - - try { - for (let i = 0; i < 5; i++) { - await worker.handle(generateTask()); - } - - const storedValue = await redisClient.hGet(rateLimitsKey, projectIdMock); - - expect(storedValue).not.toBeNull(); - - const [, count] = (storedValue as string).split(':'); - - expect(Number(count)).toBe(3); - } finally { - nowSpy.mockRestore(); - } - }); - - test('falls back to plan limits when workspace settings are empty', async () => { - await setPlanRateLimit(4, 60); - await setWorkspaceRateLimit(0, 0); - await setProjectRateLimit(0, 0); - - const currentTime = 6_000_000; - const nowSpy = jest.spyOn(Date, 'now').mockImplementation(() => currentTime); - - try { - for (let i = 0; i < 6; i++) { - await worker.handle(generateTask()); - } - - const storedValue = await redisClient.hGet(rateLimitsKey, projectIdMock); - - expect(storedValue).not.toBeNull(); - - const [, count] = (storedValue as string).split(':'); - - expect(Number(count)).toBe(4); - } finally { - nowSpy.mockRestore(); - } - }); - - test('prefers project limits over workspace and plan', async () => { - await setPlanRateLimit(4, 60); - await setWorkspaceRateLimit(6, 60); - await setProjectRateLimit(8, 60); - - const currentTime = 7_000_000; - const nowSpy = jest.spyOn(Date, 'now').mockImplementation(() => currentTime); - - try { - for (let i = 0; i < 10; i++) { - await worker.handle(generateTask()); - } - - const storedValue = await redisClient.hGet(rateLimitsKey, projectIdMock); - - expect(storedValue).not.toBeNull(); - - const [, count] = (storedValue as string).split(':'); - - expect(Number(count)).toBe(8); - } finally { - nowSpy.mockRestore(); - } - }); - }); - describe('Events-stored metrics', () => { test('writes minutely, hourly, and daily samples after handling an event', async () => { const safeTsAddSpy = jest.spyOn((worker as any).redis, 'safeTsAdd'); From a8a31c850928eef18b43a35b1ea21a1dc0d9273b Mon Sep 17 00:00:00 2001 From: e11sy <130844513+e11sy@users.noreply.github.com> Date: Sun, 8 Feb 2026 20:08:15 +0300 Subject: [PATCH 16/20] chore(grouper): remove redundant mocks --- workers/grouper/tests/index.test.ts | 34 ----------------------------- 1 file changed, 34 deletions(-) diff --git a/workers/grouper/tests/index.test.ts b/workers/grouper/tests/index.test.ts index 2986bf7fd..af1c2cf24 100644 --- a/workers/grouper/tests/index.test.ts +++ b/workers/grouper/tests/index.test.ts @@ -58,41 +58,16 @@ const projectIdMock = '5d206f7f9aaf7c0071d64596'; /** * Mock project data */ -const planIdMock = new mongodb.ObjectId(); -const workspaceIdMock = new mongodb.ObjectId(); - -const planMock = { - _id: planIdMock, - rateLimitSettings: { - N: 0, - T: 0, - }, -}; - -const workspaceMock = { - _id: workspaceIdMock, - tariffPlanId: planIdMock, - rateLimitSettings: { - N: 0, - T: 0, - }, -}; - const projectMock = { _id: new mongodb.ObjectId(projectIdMock), id: projectIdMock, name: 'Test Project', token: 'test-token', - workspaceId: workspaceIdMock, uidAdded: { id: 'test-user-id', }, unreadCount: 0, description: 'Test project for grouper worker tests', - rateLimitSettings: { - N: 0, - T: 0, - }, eventGroupingPatterns: [ { _id: mongodb.ObjectId(), pattern: 'New error .*', @@ -139,8 +114,6 @@ describe('GrouperWorker', () => { let dailyEventsCollection: Collection; let repetitionsCollection: Collection; let projectsCollection: Collection; - let workspacesCollection: Collection; - let plansCollection: Collection; let redisClient: RedisClientType; let worker: GrouperWorker; beforeAll(async () => { @@ -160,17 +133,12 @@ describe('GrouperWorker', () => { dailyEventsCollection = connection.db().collection('dailyEvents:' + projectIdMock); repetitionsCollection = connection.db().collection('repetitions:' + projectIdMock); projectsCollection = accountsConnection.db().collection('projects'); - workspacesCollection = accountsConnection.db().collection('workspaces'); - plansCollection = accountsConnection.db().collection('plans'); /** * Create unique index for groupHash */ await eventsCollection.createIndex({ groupHash: 1 }, { unique: true }); - await plansCollection.insertOne(planMock); - await workspacesCollection.insertOne(workspaceMock); - /** * Insert mock project into accounts database */ @@ -857,8 +825,6 @@ describe('GrouperWorker', () => { await redisClient.quit(); await worker.finish(); await projectsCollection.deleteMany({}); - await workspacesCollection.deleteMany({}); - await plansCollection.deleteMany({}); await accountsConnection.close(); await connection.close(); }); From f9b181d2bffb4414b3c55b66c64c90b62f23af74 Mon Sep 17 00:00:00 2001 From: e11sy <130844513+e11sy@users.noreply.github.com> Date: Sun, 8 Feb 2026 20:09:47 +0300 Subject: [PATCH 17/20] chore(): eslint fix --- workers/grouper/src/redisHelper.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/workers/grouper/src/redisHelper.ts b/workers/grouper/src/redisHelper.ts index cc009cd59..d15df9403 100644 --- a/workers/grouper/src/redisHelper.ts +++ b/workers/grouper/src/redisHelper.ts @@ -2,7 +2,6 @@ import HawkCatcher from '@hawk.so/nodejs'; import type { RedisClientType } from 'redis'; import { createClient } from 'redis'; import createLogger from '../../../lib/logger'; -import { MS_IN_SEC } from '../../../lib/utils/consts'; /** * Class with helper functions for working with Redis From a583997f84be9436e0e1ef82e18073365a1cb6e5 Mon Sep 17 00:00:00 2001 From: e11sy <130844513+e11sy@users.noreply.github.com> Date: Sun, 8 Feb 2026 21:12:44 +0300 Subject: [PATCH 18/20] chore(): change metric type --- workers/grouper/src/index.ts | 2 +- workers/grouper/tests/index.test.ts | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/workers/grouper/src/index.ts b/workers/grouper/src/index.ts index 05aff09b0..a0611383e 100644 --- a/workers/grouper/src/index.ts +++ b/workers/grouper/src/index.ts @@ -270,7 +270,7 @@ export default class GrouperWorker extends Worker { } } - await this.recordProjectMetrics(task.projectId, 'events-stored'); + await this.recordProjectMetrics(task.projectId, 'events-accepted'); } /** diff --git a/workers/grouper/tests/index.test.ts b/workers/grouper/tests/index.test.ts index af1c2cf24..6ad01812d 100644 --- a/workers/grouper/tests/index.test.ts +++ b/workers/grouper/tests/index.test.ts @@ -743,7 +743,7 @@ describe('GrouperWorker', () => { }); }); - describe('Events-stored metrics', () => { + describe('Events-accepted metrics', () => { test('writes minutely, hourly, and daily samples after handling an event', async () => { const safeTsAddSpy = jest.spyOn((worker as any).redis, 'safeTsAdd'); @@ -754,27 +754,27 @@ describe('GrouperWorker', () => { const expectedLabels = { type: 'error', - status: 'events-stored', + status: 'events-accepted', project: projectIdMock, }; expect(safeTsAddSpy).toHaveBeenNthCalledWith( 1, - `ts:project-events-stored:${projectIdMock}:minutely`, + `ts:project-events-accepted:${projectIdMock}:minutely`, 1, expectedLabels, TimeMs.DAY ); expect(safeTsAddSpy).toHaveBeenNthCalledWith( 2, - `ts:project-events-stored:${projectIdMock}:hourly`, + `ts:project-events-accepted:${projectIdMock}:hourly`, 1, expectedLabels, TimeMs.WEEK ); expect(safeTsAddSpy).toHaveBeenNthCalledWith( 3, - `ts:project-events-stored:${projectIdMock}:daily`, + `ts:project-events-accepted:${projectIdMock}:daily`, 1, expectedLabels, 90 * TimeMs.DAY @@ -799,7 +799,7 @@ describe('GrouperWorker', () => { try { await worker.handle(generateTask()); - expect(loggerErrorSpy).toHaveBeenCalledWith('Failed to add hourly TS for events-stored', failure); + expect(loggerErrorSpy).toHaveBeenCalledWith('Failed to add hourly TS for events-accepted', failure); expect(await eventsCollection.find().count()).toBe(1); } finally { safeTsAddSpy.mockRestore(); @@ -814,7 +814,7 @@ describe('GrouperWorker', () => { await worker.handle(generateTask()); expect(recordMetricsSpy).toHaveBeenCalledTimes(1); - expect(recordMetricsSpy).toHaveBeenCalledWith(projectIdMock, 'events-stored'); + expect(recordMetricsSpy).toHaveBeenCalledWith(projectIdMock, 'events-accepted'); } finally { recordMetricsSpy.mockRestore(); } From 8ed970c057e680650a0a8ed52c63df64273a0f9f Mon Sep 17 00:00:00 2001 From: e11sy <130844513+e11sy@users.noreply.github.com> Date: Sun, 8 Feb 2026 21:44:50 +0300 Subject: [PATCH 19/20] Update workers/grouper/src/index.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- workers/grouper/src/index.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/workers/grouper/src/index.ts b/workers/grouper/src/index.ts index a0611383e..d0bdb4e65 100644 --- a/workers/grouper/src/index.ts +++ b/workers/grouper/src/index.ts @@ -324,13 +324,19 @@ export default class GrouperWorker extends Worker { }, ]; - for (const { key, label, retentionMs } of series) { - try { - await this.redis.safeTsAdd(key, 1, labels, retentionMs); - } catch (error) { - this.logger.error(`Failed to add ${label} TS for ${metricType}`, error); + const operations = series.map(({ key, label, retentionMs }) => ({ + label, + promise: this.redis.safeTsAdd(key, 1, labels, retentionMs), + })); + + const results = await Promise.allSettled(operations.map((op) => op.promise)); + + results.forEach((result, index) => { + if (result.status === 'rejected') { + const { label } = operations[index]; + this.logger.error(`Failed to add ${label} TS for ${metricType}`, result.reason); } - } + }); } /** From 45e527abb003c8d8ef5da53f044b9ffb84ed9089 Mon Sep 17 00:00:00 2001 From: e11sy <130844513+e11sy@users.noreply.github.com> Date: Sun, 8 Feb 2026 22:06:29 +0300 Subject: [PATCH 20/20] imp(): use lua for create if not exists, to avoid race-cond --- workers/grouper/src/redisHelper.ts | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/workers/grouper/src/redisHelper.ts b/workers/grouper/src/redisHelper.ts index d15df9403..ec486ddb0 100644 --- a/workers/grouper/src/redisHelper.ts +++ b/workers/grouper/src/redisHelper.ts @@ -122,13 +122,16 @@ export default class RedisHelper { labels: Record, retentionMs = 0 ): Promise { - const exists = await this.redisClient.exists(key); + const script = ` + if redis.call('EXISTS', KEYS[1]) == 1 then + return 0 + end - if (exists > 0) { - return; - } + redis.call('TS.CREATE', KEYS[1], unpack(ARGV)) + return 1 + `; - const args: string[] = ['TS.CREATE', key]; + const args: string[] = []; if (retentionMs > 0) { args.push('RETENTION', Math.floor(retentionMs).toString()); @@ -136,7 +139,13 @@ export default class RedisHelper { args.push(...this.buildLabelArguments(labels)); - await this.redisClient.sendCommand(args); + await this.redisClient.eval( + script, + { + keys: [key], + arguments: args, + } + ); } /**