From 3ba9cb9e09c6d5ceff91f42a92df2b0f3544ac40 Mon Sep 17 00:00:00 2001 From: Caleb McQuaid Date: Mon, 6 Jul 2026 21:56:19 -0400 Subject: [PATCH 01/57] feat: reviewer skip via Redis skip set, atomic logSkip --- .../manualReviewToolService.ts | 18 ++ .../QueueOperations.reviewerSkips.test.ts | 200 ++++++++++++++++++ .../modules/QueueOperations.ts | 133 +++++++++--- 3 files changed, 316 insertions(+), 35 deletions(-) create mode 100644 server/services/manualReviewToolService/modules/QueueOperations.reviewerSkips.test.ts diff --git a/server/services/manualReviewToolService/manualReviewToolService.ts b/server/services/manualReviewToolService/manualReviewToolService.ts index 49ed67a7..a9673ed1 100644 --- a/server/services/manualReviewToolService/manualReviewToolService.ts +++ b/server/services/manualReviewToolService/manualReviewToolService.ts @@ -1660,6 +1660,8 @@ export class ManualReviewToolService { return this.queueOps.updateHiddenActionsForQueue(opts); } + // Skipping is one server-side operation so the client can't end up with + // half a skip (e.g. lock released but skip not recorded). async logSkip(opts: { orgId: string; queueId: string; @@ -1667,6 +1669,22 @@ export class ManualReviewToolService { userId: string; }) { await this.skipOps.logSkip(opts); + // Hide the job from THIS reviewer for the skip window; the dequeue path + // reads this back and steps past it. + await this.queueOps.recordReviewerSkip({ + orgId: opts.orgId, + queueId: opts.queueId, + reviewerId: opts.userId, + jobId: opts.jobId, + }); + // Release the reviewer's lock (the lock token is the reviewer's userId) + // so the job returns to the shared pool immediately for everyone else. + await this.releaseJobLock({ + orgId: opts.orgId, + queueId: opts.queueId, + jobId: opts.jobId, + lockToken: opts.userId, + }); } async releaseJobLock(opts: { diff --git a/server/services/manualReviewToolService/modules/QueueOperations.reviewerSkips.test.ts b/server/services/manualReviewToolService/modules/QueueOperations.reviewerSkips.test.ts new file mode 100644 index 00000000..fe5bcadb --- /dev/null +++ b/server/services/manualReviewToolService/modules/QueueOperations.reviewerSkips.test.ts @@ -0,0 +1,200 @@ +import { uid } from 'uid'; + +import getBottle from '../../../iocContainer/index.js'; +import createMrtQueue from '../../../test/fixtureHelpers/createMrtQueue.js'; +import createOrg from '../../../test/fixtureHelpers/createOrg.js'; +import createUser from '../../../test/fixtureHelpers/createUser.js'; +import { makeTestWithFixture } from '../../../test/utils.js'; +import { instantiateOpaqueType } from '../../../utils/typescript-types.js'; +import { + makeSubmissionId, + type NormalizedItemData, +} from '../../itemProcessingService/index.js'; +import { type ItemSubmissionWithTypeIdentifier } from '../../itemProcessingService/makeItemSubmissionWithTypeIdentifier.js'; +import { type ManualReviewJobPayload } from '../manualReviewToolService.js'; + +describe('QueueOperations per-reviewer skips', () => { + const testWithQueue = () => + makeTestWithFixture(async () => { + const container = (await getBottle()).container; + + const { org, cleanup: orgCleanup } = await createOrg( + { + KyselyPg: container.KyselyPg, + ModerationConfigService: container.ModerationConfigService, + ApiKeyService: container.ApiKeyService, + }, + uid(), + ); + + const { user, cleanup: userCleanup } = await createUser( + container.KyselyPg, + org.id, + ); + + const { queue, cleanup: queuesCleanup } = await createMrtQueue({ + orgId: org.id, + mrtService: container.ManualReviewToolService, + userId: user.id, + }); + + return { + org, + queue, + user, + mrtService: container.ManualReviewToolService, + cleanup: async () => { + await queuesCleanup(); + await userCleanup(); + await orgCleanup(); + await container.KyselyPg.destroy(); + await container.KyselyPgReadReplica.destroy(); + }, + }; + }); + + const makePayloadFor = + (itemTypeId: string) => + (itemId: string): ManualReviewJobPayload => ({ + kind: 'DEFAULT', + reportHistory: [], + reportedForReasons: [], + item: instantiateOpaqueType({ + submissionId: makeSubmissionId(), + submissionTime: new Date(), + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions + data: {} as NormalizedItemData, + itemTypeIdentifier: { + id: itemTypeId, + version: new Date().toISOString(), + schemaVariant: 'original', + }, + creator: { id: uid(), typeId: uid() }, + itemId, + }), + enqueueSourceInfo: { kind: 'REPORT' }, + }); + + testWithQueue()( + 'a skipped job is hidden from that reviewer but immediately available to others', + async ({ org, queue, mrtService }) => { + const queueOps = mrtService['queueOps']; + const payloadFor = makePayloadFor(uid()); + + const xJob = await queueOps.addJob({ + orgId: org.id, + queueId: queue.id, + enqueueSourceInfo: { kind: 'REPORT' }, + priority: 1000, + jobPayload: { policyIds: [], payload: payloadFor('item-X') }, + }); + await queueOps.addJob({ + orgId: org.id, + queueId: queue.id, + enqueueSourceInfo: { kind: 'REPORT' }, + priority: 2000, + jobPayload: { policyIds: [], payload: payloadFor('item-Y') }, + }); + + await queueOps.recordReviewerSkip({ + orgId: org.id, + queueId: queue.id, + reviewerId: 'reviewer-a', + jobId: xJob.id, + }); + + const aJob = await queueOps.dequeueNextJobWithLock({ + orgId: org.id, + queueId: queue.id, + lockToken: 'reviewer-a', + }); + expect(aJob?.job.payload.item.itemId).toBe('item-Y'); + + const bJob = await queueOps.dequeueNextJobWithLock({ + orgId: org.id, + queueId: queue.id, + lockToken: 'reviewer-b', + }); + expect(bJob?.job.payload.item.itemId).toBe('item-X'); + }, + ); + + testWithQueue()( + 'a queue whose only jobs are skipped returns null instead of hanging', + async ({ org, queue, mrtService }) => { + const queueOps = mrtService['queueOps']; + const payloadFor = makePayloadFor(uid()); + + const onlyJob = await queueOps.addJob({ + orgId: org.id, + queueId: queue.id, + enqueueSourceInfo: { kind: 'REPORT' }, + priority: 1000, + jobPayload: { policyIds: [], payload: payloadFor('item-X') }, + }); + await queueOps.recordReviewerSkip({ + orgId: org.id, + queueId: queue.id, + reviewerId: 'reviewer-a', + jobId: onlyJob.id, + }); + + const result = await queueOps.dequeueNextJobWithLock({ + orgId: org.id, + queueId: queue.id, + lockToken: 'reviewer-a', + }); + expect(result).toBeNull(); + }, + ); + + testWithQueue()( + 'logSkip hides the job from the skipper and releases their lock in one call', + async ({ org, queue, user, mrtService }) => { + const queueOps = mrtService['queueOps']; + const payloadFor = makePayloadFor(uid()); + + await queueOps.addJob({ + orgId: org.id, + queueId: queue.id, + enqueueSourceInfo: { kind: 'REPORT' }, + priority: 1000, + jobPayload: { policyIds: [], payload: payloadFor('item-X') }, + }); + await queueOps.addJob({ + orgId: org.id, + queueId: queue.id, + enqueueSourceInfo: { kind: 'REPORT' }, + priority: 2000, + jobPayload: { policyIds: [], payload: payloadFor('item-Y') }, + }); + + const first = await queueOps.dequeueNextJobWithLock({ + orgId: org.id, + queueId: queue.id, + lockToken: user.id, + }); + expect(first?.job.payload.item.itemId).toBe('item-X'); + await mrtService.logSkip({ + orgId: org.id, + queueId: queue.id, + jobId: first!.job.id, + userId: user.id, + }); + + const other = await queueOps.dequeueNextJobWithLock({ + orgId: org.id, + queueId: queue.id, + lockToken: 'reviewer-b', + }); + expect(other?.job.payload.item.itemId).toBe('item-X'); + + const next = await queueOps.dequeueNextJobWithLock({ + orgId: org.id, + queueId: queue.id, + lockToken: user.id, + }); + expect(next?.job.payload.item.itemId).toBe('item-Y'); + }, + ); +}); diff --git a/server/services/manualReviewToolService/modules/QueueOperations.ts b/server/services/manualReviewToolService/modules/QueueOperations.ts index 0fbbd1ec..761e1344 100644 --- a/server/services/manualReviewToolService/modules/QueueOperations.ts +++ b/server/services/manualReviewToolService/modules/QueueOperations.ts @@ -163,7 +163,7 @@ export default class QueueOperations { private readonly pgQuery: Kysely, private readonly pgQueryReadReplica: Kysely, private readonly moderationConfigService: Dependencies['ModerationConfigService'], - redis: RedisConnection, + private readonly redis: RedisConnection, ) { this.transactionWithRetry = makeKyselyTransactionWithRetry(this.pgQuery); // Reassingment here is a hack to work around TS syntax limitations @@ -1253,7 +1253,9 @@ export default class QueueOperations { let hasDecision = true; while (hasDecision) { - const job = await worker.getNextJob(lockToken); + // block: false so a drained queue returns null immediately instead of + // long-polling and hanging the reviewer's request. + const job = await worker.getNextJob(lockToken, { block: false }); if (!job) { return null; @@ -1304,47 +1306,104 @@ export default class QueueOperations { await this.checkQueueExists(orgId, queueId); const worker = await this.getBullWorker({ orgId, queueId }); - let hasDecision = true; - while (hasDecision) { - const job = await worker.getNextJob(lockToken); - - if (!job) { - return null; - } + // Jobs this reviewer skipped within the skip window (the lock token is + // the reviewer's userId). The scan steps past them by keeping them locked + // until it finishes, then releases them in `finally` so they return to + // the shared pool — a skip is per-reviewer, not global. + const reviewerSkips = await this.getActiveReviewerSkips({ + orgId, + queueId, + reviewerId: lockToken, + }); + const heldAside: Job[] = []; - const convertedJob = await this.legacyJobToJob(job, orgId); + try { + while (true) { + const job = await worker.getNextJob(lockToken, { block: false }); - // There is a race condition due to the locking mechanism where a job can - // be decided on but not dequeued, so we check here if the first job in the - // queue has a decision, and if so use the lock token to immediately - // remove it, then grab a new job and return to the caller. it is very - // unlikely that there are multiple jobs like this at the front of the - // queue, but not impossible. - const decision = await this.pgQueryReadReplica - .selectFrom('manual_review_tool.manual_review_decisions') - .select(['decision_components']) // not really necessary to return anything - .where('created_at', '>=', new Date('2023-10-01')) - .where('org_id', '=', orgId) - .where('id', '=', jobIdToGuid(convertedJob.data.id)) - .executeTakeFirst(); + if (!job) { + return null; + } + if (reviewerSkips.has(job.data.id)) { + heldAside.push(job); + continue; + } - hasDecision = decision !== undefined; + const convertedJob = await this.legacyJobToJob(job, orgId); + + // There is a race condition due to the locking mechanism where a job can + // be decided on but not dequeued, so we check here if the first job in the + // queue has a decision, and if so use the lock token to immediately + // remove it, then grab a new job and return to the caller. it is very + // unlikely that there are multiple jobs like this at the front of the + // queue, but not impossible. + const decision = await this.pgQueryReadReplica + .selectFrom('manual_review_tool.manual_review_decisions') + .select(['decision_components']) // not really necessary to return anything + .where('created_at', '>=', new Date('2023-10-01')) + .where('org_id', '=', orgId) + .where('id', '=', jobIdToGuid(convertedJob.data.id)) + .executeTakeFirst(); - if (hasDecision) { - await this.removeJob({ + if (decision !== undefined) { + await this.removeJob({ + orgId, + queueId, + lockToken, + jobId: convertedJob.data.id, + }).catch(() => {}); + // then continue while loop + } else { + // this is the most likely case, where there is a job + // and it has never been decided before + return { job: convertedJob.data, lockToken }; + } + } + } finally { + // Release the held-aside jobs so other reviewers can pick them up + // immediately. This reviewer stays excluded via the skip set. + for (const held of heldAside) { + await this.releaseJobLock({ orgId, queueId, + jobId: held.data.id, lockToken, - jobId: convertedJob.data.id, }).catch(() => {}); - // then continue while loop - } else { - // this is the most likely case, where there is a job - // and it has never been decided before - return { job: convertedJob.data, lockToken }; } } - return null; + } + + static readonly REVIEWER_SKIP_TTL_MS = 30 * 60 * 1000; + + #reviewerSkipKey(orgId: string, queueId: string, reviewerId: string): string { + return `{${orgId}}:mrt-reviewer-skips:${queueId}:${reviewerId}`; + } + + async recordReviewerSkip(opts: { + orgId: string; + queueId: string; + reviewerId: string; + jobId: string; + }): Promise { + const { orgId, queueId, reviewerId, jobId } = opts; + const key = this.#reviewerSkipKey(orgId, queueId, reviewerId); + const expiresAt = Date.now() + QueueOperations.REVIEWER_SKIP_TTL_MS; + await this.redis.zadd(key, expiresAt, jobId); + // Backstop: the whole set disappears once everything in it has expired. + await this.redis.pexpire(key, QueueOperations.REVIEWER_SKIP_TTL_MS); + } + + async getActiveReviewerSkips(opts: { + orgId: string; + queueId: string; + reviewerId: string; + }): Promise> { + const { orgId, queueId, reviewerId } = opts; + const key = this.#reviewerSkipKey(orgId, queueId, reviewerId); + // Drop expired entries, then read what's still active. + await this.redis.zremrangebyscore(key, 0, Date.now()); + const ids = await this.redis.zrange(key, 0, -1); + return new Set(ids); } /** @@ -1942,9 +2001,13 @@ export async function getBullWorker( await worker.startStalledCheckTimer(); // Cast worker to a version of its original type, but fixed to correctly - // indicate that getNextJob() can return undefined + // indicate that getNextJob() can return undefined and accepts a `block` + // option (false = return immediately instead of long-polling). return worker as unknown as Omit, 'getNextJob'> & { - getNextJob: (lockToken: string) => Promise | undefined>; + getNextJob: ( + lockToken: string, + opts?: { block?: boolean }, + ) => Promise | undefined>; }; } From 6f7e7f9c0408b0a3ffed15b181728e2cdacacd0e Mon Sep 17 00:00:00 2001 From: Caleb McQuaid Date: Mon, 6 Jul 2026 22:49:41 -0400 Subject: [PATCH 02/57] feat: single call skip with failure modal, drained queue redirect --- client/src/graphql/generated.ts | 58 -------------- .../ManualReviewJobReview.tsx | 77 +++++++++---------- .../manualReviewToolService.ts | 2 - 3 files changed, 38 insertions(+), 99 deletions(-) diff --git a/client/src/graphql/generated.ts b/client/src/graphql/generated.ts index 9211df45..0923f087 100644 --- a/client/src/graphql/generated.ts +++ b/client/src/graphql/generated.ts @@ -15409,15 +15409,6 @@ export type GQLLogSkipMutation = { readonly logSkip: boolean; }; -export type GQLReleaseJobLockMutationVariables = Exact<{ - input: GQLReleaseJobLockInput; -}>; - -export type GQLReleaseJobLockMutation = { - readonly __typename: 'Mutation'; - readonly releaseJobLock: boolean; -}; - export type GQLJobFieldsFragment = { readonly __typename: 'ManualReviewJob'; readonly id: string; @@ -35024,54 +35015,6 @@ export type GQLLogSkipMutationOptions = Apollo.BaseMutationOptions< GQLLogSkipMutation, GQLLogSkipMutationVariables >; -export const GQLReleaseJobLockDocument = gql` - mutation ReleaseJobLock($input: ReleaseJobLockInput!) { - releaseJobLock(input: $input) - } -`; -export type GQLReleaseJobLockMutationFn = Apollo.MutationFunction< - GQLReleaseJobLockMutation, - GQLReleaseJobLockMutationVariables ->; - -/** - * __useGQLReleaseJobLockMutation__ - * - * To run a mutation, you first call `useGQLReleaseJobLockMutation` within a React component and pass it any options that fit your needs. - * When your component renders, `useGQLReleaseJobLockMutation` returns a tuple that includes: - * - A mutate function that you can call at any time to execute the mutation - * - An object with fields that represent the current status of the mutation's execution - * - * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; - * - * @example - * const [gqlReleaseJobLockMutation, { data, loading, error }] = useGQLReleaseJobLockMutation({ - * variables: { - * input: // value for 'input' - * }, - * }); - */ -export function useGQLReleaseJobLockMutation( - baseOptions?: Apollo.MutationHookOptions< - GQLReleaseJobLockMutation, - GQLReleaseJobLockMutationVariables - >, -) { - const options = { ...defaultOptions, ...baseOptions }; - return Apollo.useMutation< - GQLReleaseJobLockMutation, - GQLReleaseJobLockMutationVariables - >(GQLReleaseJobLockDocument, options); -} -export type GQLReleaseJobLockMutationHookResult = ReturnType< - typeof useGQLReleaseJobLockMutation ->; -export type GQLReleaseJobLockMutationResult = - Apollo.MutationResult; -export type GQLReleaseJobLockMutationOptions = Apollo.BaseMutationOptions< - GQLReleaseJobLockMutation, - GQLReleaseJobLockMutationVariables ->; export const GQLGetRelatedItemsDocument = gql` query getRelatedItems($itemIdentifiers: [ItemIdentifierInput!]!) { latestItemSubmissions(itemIdentifiers: $itemIdentifiers) { @@ -45764,7 +45707,6 @@ export const namedOperations = { DequeueManualReviewJob: 'DequeueManualReviewJob', SubmitManualReviewDecision: 'SubmitManualReviewDecision', LogSkip: 'LogSkip', - ReleaseJobLock: 'ReleaseJobLock', AddJobComment: 'AddJobComment', DeleteJobComment: 'DeleteJobComment', DeleteRoutingRule: 'DeleteRoutingRule', diff --git a/client/src/webpages/dashboard/mrt/manual_review_job/ManualReviewJobReview.tsx b/client/src/webpages/dashboard/mrt/manual_review_job/ManualReviewJobReview.tsx index 7f528b20..19936440 100644 --- a/client/src/webpages/dashboard/mrt/manual_review_job/ManualReviewJobReview.tsx +++ b/client/src/webpages/dashboard/mrt/manual_review_job/ManualReviewJobReview.tsx @@ -39,7 +39,6 @@ import { useGQLDequeueManualReviewJobMutation, useGQLLogSkipMutation, useGQLManualReviewJobInfoQuery, - useGQLReleaseJobLockMutation, useGQLSubmitManualReviewDecisionMutation, type GQLActionParameter, type GQLThreadManualReviewJobPayload, @@ -221,10 +220,6 @@ gql` mutation LogSkip($input: LogSkipInput!) { logSkip(input: $input) } - - mutation ReleaseJobLock($input: ReleaseJobLockInput!) { - releaseJobLock(input: $input) - } `; enum BuiltInActionType { @@ -342,17 +337,18 @@ function ManualReviewJobReviewImpl(props: { const actionStore = useContext(ManualReviewActionStore); - const setSelectedRelatedActions = ( - actions: ManualReviewJobEnqueuedActionData[], - ) => { - actionStore?.setActions( - actions.map((it) => ({ - itemId: it.target.identifier.itemId, - action: it.action, - })), - ); - selectedRelatedActionsSetter(actions); - }; + const setSelectedRelatedActions = useCallback( + (actions: ManualReviewJobEnqueuedActionData[]) => { + actionStore?.setActions( + actions.map((it) => ({ + itemId: it.target.identifier.itemId, + action: it.action, + })), + ); + selectedRelatedActionsSetter(actions); + }, + [actionStore], + ); const { queueId, jobId, lockToken } = useParams<{ queueId?: string; @@ -364,12 +360,12 @@ function ManualReviewJobReviewImpl(props: { const mrtParentComponentRef = useRef(null); const reportedUserRef = useRef(null); - const resetState = () => { + const resetState = useCallback(() => { setSelectedPrimaryActions([]); setSelectedPrimaryPolicies([]); setSelectedRelatedActions([]); setDecisionReason(undefined); - }; + }, [setSelectedRelatedActions]); const { data, @@ -389,9 +385,12 @@ function ManualReviewJobReviewImpl(props: { onCompleted: (data) => { // Here, we update the URL to include the queue ID, job ID, and lock // token. That way, users are able to send around the URL to others. - // In case we can't find the required job, we can just fail silently. const { dequeueManualReviewJob } = data; if (dequeueManualReviewJob == null) { + // No reviewable job: the queue is drained, or everything left is + // skipped by this reviewer. Send them back to the queue list instead + // of leaving them on a perpetual loading spinner. + navigate('/dashboard/manual_review/queues', { replace: true }); return; } @@ -739,10 +738,6 @@ function ManualReviewJobReviewImpl(props: { fetchPolicy: 'no-cache', }); - const [releaseJobLock] = useGQLReleaseJobLockMutation({ - fetchPolicy: 'no-cache', - }); - const advanceToNextJobAfterInvalidation = useCallback(async () => { setIsAdvancingToNextJob(true); try { @@ -754,8 +749,7 @@ function ManualReviewJobReviewImpl(props: { } finally { setIsAdvancingToNextJob(false); } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [getNextJob, navigate]); + }, [getNextJob, navigate, resetState]); // Runs after the invalidate mutation resolves. Refreshes the job view, // and if invalidation deleted the current job, advances to the next one. @@ -783,20 +777,21 @@ function ManualReviewJobReviewImpl(props: { }, [jobId, queueId, refetchJobInfo, advanceToNextJobAfterInvalidation]); const skipToNextJob = async () => { - // First, release the lock on the current job and log the skip + // Skipping is one server-side operation: it logs the skip, hides the job + // from this reviewer for the skip window, and releases the lock so the + // job returns to the shared pool for everyone else. if (queueId && job?.id && lockToken) { - await Promise.all([ - logSkip(), - releaseJobLock({ - variables: { - input: { - queueId, - jobId: job.id, - lockToken, - }, - }, - }), - ]); + const result = await logSkip(); + if (result.data?.logSkip !== true) { + // Nothing was released or hidden; stay on the current job so the + // reviewer can retry (or decide) instead of advancing past it. + setModalInfo({ + visible: true, + modalBody: 'Failed to skip this job. Please try again.', + footer: [{ title: 'Ok', type: 'primary', onClick: hideModal }], + }); + return; + } } // Reset state and try to get the next job @@ -809,7 +804,11 @@ function ManualReviewJobReviewImpl(props: { } }; - if (loading || jobDataLoading || (!closedJob && !lockToken)) { + // `loading && !data`: the job-info query reloads in place (e.g. when its + // jobIds variable changes while advancing, or on refetch after an + // invalidation). Once we have data, keep the current view up during those + // reloads instead of flashing the full-screen spinner. + if ((loading && !data) || jobDataLoading || (!closedJob && !lockToken)) { return (
diff --git a/server/services/manualReviewToolService/manualReviewToolService.ts b/server/services/manualReviewToolService/manualReviewToolService.ts index a9673ed1..2c4095a1 100644 --- a/server/services/manualReviewToolService/manualReviewToolService.ts +++ b/server/services/manualReviewToolService/manualReviewToolService.ts @@ -1660,8 +1660,6 @@ export class ManualReviewToolService { return this.queueOps.updateHiddenActionsForQueue(opts); } - // Skipping is one server-side operation so the client can't end up with - // half a skip (e.g. lock released but skip not recorded). async logSkip(opts: { orgId: string; queueId: string; From ef18452a3cc772c346163542606b79c51b8c1eb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tao=20Bojl=C3=A9n?= Date: Tue, 7 Jul 2026 14:43:21 +0100 Subject: [PATCH 03/57] add NCMEC field + integration tests (#887) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * test(ncmec): add field coverage and submission tests Add XML field-coverage regression tests for NCMEC report builders, including XSD-order locks for the ipCaptureEvent ordering regression class. Add a submitReport integration test using a stubbed fetchHTTP seam against real Postgres, plus a fixture helper for NCMEC org settings. Canonicalise webhook-sourced ipCaptureEvent objects before XML rendering so webhook key order cannot produce out-of-order CyberTip XML. Co-Authored-By: pi * fix(ncmec): correct fileDetails ipCaptureEvent XSD ordering; harden tests buildFileDetailsObject emits in the wrong position relative to /. PR #869 moved it BEFORE industryClassification, claiming NCMEC's live validator required that — but a probe against exttest.cybertip.org proves the opposite: - Variant A (#869 order, ipCaptureEvent BEFORE industryClassification): REJECTED — cvc-complex-type.2.4.a: Invalid content was found starting with element 'industryClassification'. One of '{deviceId, details, additionalInfo}' is expected. - Variant B (canonical XSD order, ipCaptureEvent AFTER industryClassification/originalFileHash): ACCEPTED, responseCode=0. #869 misread the original #856 error (which fires when an element lands in the trailing deviceId/details/additionalInfo slot) and moved ipCaptureEvent backwards to a position NCMEC actually rejects. #869's own PR description admits the live re-test was never run before merge. This commit restores the canonical XSD order, which the live validator accepts. Reorders buildFileDetailsObject and the FileDetails type so ipCaptureEvent follows industryClassification and originalFileHash, and updates the ncmecReporting.builders.test.ts 'preserves XSD insertion order (Appendix C)' assertion to anchor the correct order (the test had been updated in #869 to anchor the wrong one). Also includes test-hygiene improvements to the NCMEC #843 test work: - fieldCoverage.test.ts: strengthen the renderXml assertion from a bare length>0 check to per-scenario structural XML markers (present/absent), so the js2xml serialization path is actually exercised. Remove the dead XSD.fileDetails array (buildSubmitReportObject's envelope never contains ; its ordering is locked in ncmecReporting.builders.test.ts). - ncmec-submission.integ.test.ts: extract a shared RecordedCall type, replace submitCall! non-null assertions with an early-fail type guard, and type the stub generic against the real FetchHTTP/CoopRequestQuery contract (removing the as-never param, inline cast, and final broad as-unknown-as-FetchHTTP). Requires exporting HandleResponseBody from networkingService (additive type-only export). Co-Authored-By: pi --- .../ncmecService/fieldCoverage.test.ts | 400 ++++++++++++++++++ server/services/ncmecService/index.ts | 8 +- .../ncmecReporting.builders.test.ts | 2 +- .../ncmecService/ncmecReporting.test.ts | 28 ++ .../services/ncmecService/ncmecReporting.ts | 33 +- server/services/networkingService/index.ts | 2 +- .../test/integ/ncmec-submission.integ.test.ts | 239 +++++++++++ 7 files changed, 702 insertions(+), 10 deletions(-) create mode 100644 server/services/ncmecService/fieldCoverage.test.ts create mode 100644 server/test/integ/ncmec-submission.integ.test.ts diff --git a/server/services/ncmecService/fieldCoverage.test.ts b/server/services/ncmecService/fieldCoverage.test.ts new file mode 100644 index 00000000..194b7b33 --- /dev/null +++ b/server/services/ncmecService/fieldCoverage.test.ts @@ -0,0 +1,400 @@ +/** + * Field-coverage regression net for the NCMEC CyberTip report builder. + * + * The builder emits different XML fields depending on how much data the + * adopter has about the reported user. We lock that behaviour by rendering + * the report under three configurations and asserting, per field, which ones + * populate it: + * + * - `min` — bare minimum: no user data, no webhook, no reviewer + * escalation. Only the org-setting-backed fields NCMEC + * strictly requires appear. + * - `field-roles` — the adopter maps their user data to coop's schema field + * roles (email, IP, display name, profile icon). Fields + * read off the user via roles now populate; webhook and + * reviewer-decision fields stay absent. + * - `max` — everything wired: field roles + the additional-info + * webhook + a reviewer escalation + top-level notes. + */ +import { js2xml } from 'xml-js'; + +import { + buildSubmitReportObject, + NCMECEvent, + type BuildSubmitReportObjectInput, +} from './ncmecReporting.js'; + +const INCIDENT_DATE_TIME = '2026-06-30T18:00:00.000Z'; + +/** Minimal valid inputs for `buildSubmitReportObject`. Tests override only + * the field(s) under test, so each case is self-explanatory. */ +function makeBuildReportInput( + overrides: { + reportParams?: Partial & { + reportedUser?: Partial< + BuildSubmitReportObjectInput['reportParams']['reportedUser'] + >; + }; + userAdditionalInfo?: BuildSubmitReportObjectInput['userAdditionalInfo']; + orgSettings?: Partial; + clampedIncidentDateTime?: string; + priorCTReports?: readonly number[]; + } = {}, +): BuildSubmitReportObjectInput { + const { reportParams: paramOverrides, ...rest } = overrides; + const { reportedUser: userOverrides, ...reportParamOverrides } = + paramOverrides ?? {}; + return { + reportParams: { + orgId: 'org-1', + reviewerId: 'reviewer-1', + reportedUser: { + id: 'user-1', + typeId: 'user-type-1', + ...userOverrides, + }, + media: [], + threads: [], + incidentType: + 'Child Pornography (possession, manufacture, and distribution)', + ...reportParamOverrides, + }, + userAdditionalInfo: rest.userAdditionalInfo ?? {}, + orgSettings: { + companyTemplate: 'AcmeESP', + legalURL: 'https://acme.example/legal', + reportingPersonEmail: 'reporter@acme.example', + ...rest.orgSettings, + }, + clampedIncidentDateTime: rest.clampedIncidentDateTime ?? INCIDENT_DATE_TIME, + ...(rest.priorCTReports !== undefined + ? { priorCTReports: rest.priorCTReports } + : {}), + }; +} + +// Use `any` here to avoid a ts-node crash when many path functions are typed +// against the deeply-nested Report union. +/* eslint-disable @typescript-eslint/no-explicit-any */ +type AnyReport = any; + +function renderXml(report: ReturnType): string { + return js2xml(report, { compact: true }); +} + +type ScenarioExpectations = { + min: boolean; + 'field-roles': boolean; + max: boolean; +}; + +const presenceTable: Array<{ + field: string; + path: (r: AnyReport) => unknown; + expected: ScenarioExpectations; +}> = [ + // Required by the XSD — present in every configuration (backed by org + // settings, not user data). + { + field: 'incidentSummary.incidentType', + path: (r) => r.report.incidentSummary.incidentType, + expected: { min: true, 'field-roles': true, max: true }, + }, + { + field: 'incidentSummary.incidentDateTime', + path: (r) => r.report.incidentSummary.incidentDateTime, + expected: { min: true, 'field-roles': true, max: true }, + }, + { + field: 'reporter.reportingPerson.email', + path: (r) => r.report.reporter.reportingPerson.email, + expected: { min: true, 'field-roles': true, max: true }, + }, + { + field: 'reporter.companyTemplate', + path: (r) => r.report.reporter.companyTemplate, + expected: { min: true, 'field-roles': true, max: true }, + }, + { + field: 'reporter.legalURL', + path: (r) => r.report.reporter.legalURL, + expected: { min: true, 'field-roles': true, max: true }, + }, + // These fields are read off the reported user's data via schema field roles. + // In `min` no user data is provided, so they're absent; with roles mapped + // they populate. + { + field: 'personOrUserReportedPerson.email', + path: (r) => + r.report.personOrUserReported?.personOrUserReportedPerson?.email, + expected: { min: false, 'field-roles': true, max: true }, + }, + { + field: 'personOrUserReported.displayName', + path: (r) => r.report.personOrUserReported?.displayName, + expected: { min: false, 'field-roles': true, max: true }, + }, + { + field: 'personOrUserReported.ipCaptureEvent', + path: (r) => r.report.personOrUserReported?.ipCaptureEvent, + expected: { min: false, 'field-roles': true, max: true }, + }, + // These fields come from reviewer decisions or the additional-info webhook, + // neither of which the `min` or `field-roles` scenarios provide. + { + field: 'incidentSummary.escalateToHighPriority', + path: (r) => r.report.incidentSummary.escalateToHighPriority, + expected: { min: false, 'field-roles': false, max: true }, + }, + { + field: 'report.additionalInfo', + path: (r) => r.report.additionalInfo, + expected: { min: false, 'field-roles': false, max: true }, + }, + { + field: 'personOrUserReported.priorCTReports', + path: (r) => r.report.personOrUserReported?.priorCTReports, + expected: { min: false, 'field-roles': false, max: true }, + }, + { + field: 'reporter.contactPerson', + path: (r) => r.report.reporter.contactPerson, + expected: { min: false, 'field-roles': false, max: true }, + }, + { + field: 'reporter.termsOfService', + path: (r) => r.report.reporter.termsOfService, + expected: { min: false, 'field-roles': false, max: true }, + }, + { + field: 'internetDetails.webPageIncident', + path: (r) => r.report.internetDetails?.[0]?.webPageIncident, + expected: { min: false, 'field-roles': false, max: true }, + }, +]; +/* eslint-enable @typescript-eslint/no-explicit-any */ + +describe('NCMEC field coverage', () => { + // One rendered report per configuration (see the file header for what each + // represents). The presence table below asserts which fields each one emits. + const scenarios = { + min: buildSubmitReportObject(makeBuildReportInput()), + 'field-roles': buildSubmitReportObject( + makeBuildReportInput({ + reportParams: { + reportedUser: { + id: 'user-1', + typeId: 'user-type-1', + displayName: 'Jane Doe', + profilePicture: 'https://cdn.example/jane.png', + ipAddress: '203.0.113.7', + email: 'jane@example.com', + }, + }, + }), + ), + max: buildSubmitReportObject( + makeBuildReportInput({ + reportParams: { + reportedUser: { + id: 'user-1', + typeId: 'user-type-1', + displayName: 'Jane Doe', + profilePicture: 'https://cdn.example/jane.png', + ipAddress: '203.0.113.7', + email: 'jane@example.com', + }, + escalateToHighPriority: 'immediate risk', + additionalInfo: 'top-level note', + }, + userAdditionalInfo: { + screenName: 'jane123', + email: [ + { + _text: 'jane@example.com', + _attributes: { type: 'Home', verified: true }, + }, + ], + ipCaptureEvent: [ + { + eventName: NCMECEvent.Login, + dateTime: INCIDENT_DATE_TIME, + ipAddress: '203.0.113.7', + possibleProxy: true, + port: 443, + }, + ], + }, + orgSettings: { + companyTemplate: 'AcmeESP', + legalURL: 'https://acme.example/legal', + reportingPersonEmail: 'reporter@acme.example', + contactPersonEmail: 'contact@acme.example', + contactPersonFirstName: 'Cmp', + contactPersonLastName: 'Last', + contactPersonPhone: '+15555550100', + termsOfService: 'do not be evil', + defaultInternetDetailType: 'WEB_PAGE', + moreInfoUrl: 'https://acme.example/info', + }, + priorCTReports: [123, 456], + }), + ), + }; + + type ScenarioKey = keyof typeof scenarios; + + const has = (path: (r: AnyReport) => unknown, key: ScenarioKey): boolean => + path(scenarios[key]) !== undefined; + + describe('presence table', () => { + for (const row of presenceTable) { + it(`emits ${row.field} per scenario expectations`, () => { + for (const key of ['min', 'field-roles', 'max'] as ScenarioKey[]) { + expect(has(row.path, key)).toBe(row.expected[key]); + } + }); + } + }); + + it('renders all three scenarios to well-formed XML with scenario-specific markers', () => { + // The presence table checks the object tree; this is the only test that + // exercises the js2xml serialization path, so assert meaningful structural + // and field markers per scenario rather than just non-empty output. + const markers: Record< + ScenarioKey, + { present: string[]; absent: string[] } + > = { + // min: only org-setting-backed required fields — no user data, no + // escalation, no top-level additionalInfo. personOrUserReported is + // always emitted (espIdentifier/espService) but carries no IP event, + // display name, or prior reports. + min: { + present: [ + '', + '', + '', + '', + '', + '', + '', + ], + absent: [ + '', + '', + '', + '', + ], + }, + // field-roles: user data via roles populates personOrUserReported + IP + // event, but still no escalation or top-level additionalInfo. + 'field-roles': { + present: [ + '', + '', + '', + 'jane@example.com', + ], + absent: ['', ''], + }, + // max: everything wired — escalation, additionalInfo, priorCTReports, + // and contactPerson all present. + max: { + present: [ + '', + '', + '', + '', + ], + absent: [], + }, + }; + for (const key of ['min', 'field-roles', 'max'] as ScenarioKey[]) { + const xml = renderXml(scenarios[key]); + expect(xml.length).toBeGreaterThan(0); + for (const marker of markers[key].present) { + expect(xml).toContain(marker); + } + for (const marker of markers[key].absent) { + expect(xml).not.toContain(marker); + } + } + }); + + // xml-js emits child elements in object-key insertion order, and NCMEC's + // XSD requires a specific sequence — out-of-order children are rejected. + // These locks fail if the builder ever emits keys in the wrong order. + describe('XSD ordering locks', () => { + const orderOf = (emitted: string[], xsdSequence: string[]): string[] => { + const idx = new Map(xsdSequence.map((k, i) => [k, i] as const)); + return emitted + .filter((k) => idx.has(k)) + .sort((a, b) => idx.get(a)! - idx.get(b)!); + }; + + const XSD = { + incidentSummary: [ + 'incidentType', + 'platform', + 'escalateToHighPriority', + 'reportAnnotations', + 'incidentDateTime', + 'incidentDateTimeDescription', + ], + personOrUserReported: [ + 'personOrUserReportedPerson', + 'vehicleDescription', + 'espIdentifier', + 'espService', + 'compromisedAccount', + 'screenName', + 'displayName', + 'profileUrl', + 'profileBio', + 'ipCaptureEvent', + 'deviceId', + 'thirdPartyUserReported', + 'priorCTReports', + 'groupIdentifier', + 'accountTemporarilyDisabled', + 'accountPermanentlyDisabled', + 'estimatedLocation', + 'allEmailsReported', + 'associatedAccount', + 'additionalInfo', + ], + ipCaptureEvent: [ + 'ipAddress', + 'eventName', + 'dateTime', + 'possibleProxy', + 'port', + ], + }; + + it('incidentSummary children follow XSD sequence', () => { + const max = scenarios.max as AnyReport; + const emitted = Object.keys(max.report.incidentSummary); + expect(orderOf(emitted, XSD.incidentSummary)).toEqual( + emitted.filter((k) => XSD.incidentSummary.includes(k)), + ); + }); + + it('personOrUserReported children follow XSD sequence', () => { + const max = scenarios.max as AnyReport; + const emitted = Object.keys(max.report.personOrUserReported); + expect(orderOf(emitted, XSD.personOrUserReported)).toEqual( + emitted.filter((k) => XSD.personOrUserReported.includes(k)), + ); + }); + + it('ipCaptureEvent children follow XSD sequence', () => { + const max = scenarios.max as AnyReport; + const evs = max.report.personOrUserReported.ipCaptureEvent; + for (const ev of evs) { + const emitted = Object.keys(ev); + expect(orderOf(emitted, XSD.ipCaptureEvent)).toEqual(emitted); + } + }); + }); +}); diff --git a/server/services/ncmecService/index.ts b/server/services/ncmecService/index.ts index 8d28de1a..bb9bf4e4 100644 --- a/server/services/ncmecService/index.ts +++ b/server/services/ncmecService/index.ts @@ -3,7 +3,13 @@ export { default as makeNcmecService, } from './ncmecService.js'; -export { NCMECIncidentType } from './ncmecReporting.js'; +export { + NCMECIncidentType, + NCMECFileAnnotation, + NCMECIndustryClassification, + type NCMECReportParams, + default as NcmecReporting, +} from './ncmecReporting.js'; export { summarizeNcmecErrorForReviewer } from './ncmecReviewerErrors.js'; export { filterDecisionsToFailedSubmissions } from './ncmecSubmissionFilters.js'; export { diff --git a/server/services/ncmecService/ncmecReporting.builders.test.ts b/server/services/ncmecService/ncmecReporting.builders.test.ts index 0f0582fa..58ccab84 100644 --- a/server/services/ncmecService/ncmecReporting.builders.test.ts +++ b/server/services/ncmecService/ncmecReporting.builders.test.ts @@ -137,9 +137,9 @@ describe('buildFileDetailsObject', () => { 'publiclyAvailable', 'fileRelevance', 'fileAnnotations', - 'ipCaptureEvent', 'industryClassification', 'originalFileHash', + 'ipCaptureEvent', 'additionalInfo', ]); }); diff --git a/server/services/ncmecService/ncmecReporting.test.ts b/server/services/ncmecService/ncmecReporting.test.ts index 9ab50407..5daa0fd3 100644 --- a/server/services/ncmecService/ncmecReporting.test.ts +++ b/server/services/ncmecService/ncmecReporting.test.ts @@ -306,6 +306,34 @@ describe('NCMEC reporting', () => { expect(webhook).toHaveLength(1); expect(params).toHaveLength(1); }); + + it('canonicalises webhook/param event key order to the XSD sequence', () => { + // A webhook returning ipCaptureEvent with keys in non-XSD order must be + // rebuilt as { ipAddress, eventName, dateTime, possibleProxy?, port? } + // before serialisation, or xml-js emits out-of-order children and NCMEC + // rejects the report with responseCode=4100. + const nonCanonicalWebhook = { + eventName: NCMECEvent.Login, + dateTime: '2026-01-01T00:00:00.000Z', + ipAddress: '192.0.2.1', + port: 443, + possibleProxy: true, + }; + const result = mergeFieldRoleIpIntoEvents( + [nonCanonicalWebhook], + undefined, + undefined, + synth, + ); + expect(result).toEqual([nonCanonicalWebhook]); + expect(Object.keys(result![0])).toEqual([ + 'ipAddress', + 'eventName', + 'dateTime', + 'possibleProxy', + 'port', + ]); + }); }); describe('resolveReportedPersonEmail', () => { diff --git a/server/services/ncmecService/ncmecReporting.ts b/server/services/ncmecService/ncmecReporting.ts index 6ee5d2c4..e6fc3982 100644 --- a/server/services/ncmecService/ncmecReporting.ts +++ b/server/services/ncmecService/ncmecReporting.ts @@ -132,9 +132,9 @@ type FileDetails = { publiclyAvailable?: boolean; fileRelevance?: 'Reported' | 'Supplemental Reported'; fileAnnotations?: FileAnnotations; - ipCaptureEvent?: IPNCMECEvent[]; industryClassification?: NCMECIndustryClassificationType; originalFileHash?: OriginalFileHash[]; + ipCaptureEvent?: IPNCMECEvent[]; deviceId?: DeviceId[]; details?: Detail[]; additionalInfo?: string[]; @@ -525,6 +525,25 @@ export function clampIncidentDateTimeToPast( }; } +/** Rebuild an ipCaptureEvent object with keys in NCMEC XSD `xs:sequence` + * order (ipAddress, eventName, dateTime, possibleProxy, port) so xml-js + * serialises the children in the order NCMEC's validator requires. Webhook + * and caller-supplied events arrive in arbitrary key order; without this, + * a non-canonical webhook event produces out-of-order XML and NCMEC rejects + * the report with responseCode=4100. Absent keys + * are omitted so optional fields stay optional. */ +function canonicaliseIpEvent(event: IPNCMECEvent): IPNCMECEvent { + const out: IPNCMECEvent = { + ipAddress: event.ipAddress, + eventName: event.eventName, + dateTime: event.dateTime, + }; + if (event.possibleProxy !== undefined) + out.possibleProxy = event.possibleProxy; + if (event.port !== undefined) out.port = event.port; + return out; +} + /** Build the `ipCaptureEvent` array for an NCMEC person or media block: * webhook events + caller-supplied events + role-IP-synthesised event, in * that order. Returns `undefined` when all sources are empty. */ @@ -546,8 +565,8 @@ export function mergeFieldRoleIpIntoEvents( const trimmedRoleIp = typeof roleIpAddress === 'string' ? roleIpAddress.trim() : ''; const events: IPNCMECEvent[] = [ - ...(webhookEvents ?? []), - ...paramEventsArray, + ...(webhookEvents ?? []).map(canonicaliseIpEvent), + ...paramEventsArray.map(canonicaliseIpEvent), ...(trimmedRoleIp !== '' ? [ { @@ -912,6 +931,10 @@ export function buildFileDetailsObject( : {}), fileRelevance, ...(fileAnnotations ? { fileAnnotations } : {}), + industryClassification: media.industryClassification, + ...(originalFileHash && originalFileHash.length > 0 + ? { originalFileHash: [...originalFileHash] } + : {}), ...(additionalInfo.ipCaptureEvent && additionalInfo.ipCaptureEvent.length > 0 ? { @@ -924,10 +947,6 @@ export function buildFileDetailsObject( })), } : {}), - industryClassification: media.industryClassification, - ...(originalFileHash && originalFileHash.length > 0 - ? { originalFileHash: [...originalFileHash] } - : {}), ...(additionalInfo.additionalInfo ? { additionalInfo: additionalInfo.additionalInfo } : {}), diff --git a/server/services/networkingService/index.ts b/server/services/networkingService/index.ts index 8d6417dc..781be027 100644 --- a/server/services/networkingService/index.ts +++ b/server/services/networkingService/index.ts @@ -34,7 +34,7 @@ type ResponseBodyMappings = { discard: undefined; }; -type HandleResponseBody = keyof ResponseBodyMappings; +export type HandleResponseBody = keyof ResponseBodyMappings; // A symbol that we can stick on FormDataLikeWithStreams objects to // unambiguously identify those objects as FormDataLikeWithStreams. diff --git a/server/test/integ/ncmec-submission.integ.test.ts b/server/test/integ/ncmec-submission.integ.test.ts new file mode 100644 index 00000000..33af41a6 --- /dev/null +++ b/server/test/integ/ncmec-submission.integ.test.ts @@ -0,0 +1,239 @@ +import 'dotenv/config'; + +import { uid } from 'uid'; +import { Headers } from 'undici'; + +import { + NCMECFileAnnotation, + NCMECIncidentType, + NCMECIndustryClassification, + NcmecReporting, + type NCMECReportParams, +} from '../../services/ncmecService/index.js'; +import { + type CoopRequestQuery, + type CoopResponse, + type FetchHTTP, + type HandleResponseBody, +} from '../../services/networkingService/index.js'; +import createOrg from '../fixtureHelpers/createOrg.js'; +import { makeTransactionalTestWithFixture } from '../harness/transactionalTest.js'; + +const MEDIA_URL = 'https://cdn.example/sample.jpg'; +const PRESERVATION_URL = 'https://preserve.example/req'; + +/** Shape of one recorded outgoing fetchHTTP call. */ +type RecordedCall = { + url: string; + method: string; + body: unknown; + headers?: Record>; +}; + +/** Records every outgoing fetchHTTP call and returns canned CyberTip + * responses. */ +function makeStubFetchHTTP( + reportId: string, + fileId: string, +): { + fetchHTTP: FetchHTTP; + calls: RecordedCall[]; +} { + const calls: RecordedCall[] = []; + const ok = (body: unknown): CoopResponse => + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- the stub returns a canned body through a slot typed by the caller's T. + ({ + status: 200, + ok: true, + headers: new Headers(), + body, + }) as CoopResponse; + const fetchHTTP: FetchHTTP = async ( + query: CoopRequestQuery, + ): Promise> => { + const { url, method, body, headers } = query; + // eslint-disable-next-line functional/immutable-data -- request recorder mutates by design + calls.push({ url, method, body, headers }); + + // media download for #upload + if (method === 'get') { + const stream = new ReadableStream({ + start(ctr) { + ctr.enqueue(new TextEncoder().encode('fake-media-bytes')); + ctr.close(); + }, + }); + return ok(stream); + } + // NCMEC CyberTip protocol — every XML endpoint returns responseCode=0. + // /submit, /upload, /fileinfo use `reportResponse`; /finish uses + // `reportDoneResponse`. + if ( + url.endsWith('/ispws/submit') || + url.endsWith('/ispws/upload') || + url.endsWith('/ispws/fileinfo') + ) { + const isSubmit = url.endsWith('/ispws/submit'); + const isUpload = url.endsWith('/ispws/upload'); + return ok({ + reportResponse: { + responseCode: { _text: '0' }, + ...(isSubmit ? { reportId: { _text: reportId } } : {}), + ...(isUpload ? { fileId: { _text: fileId } } : {}), + }, + }); + } + if (url.endsWith('/ispws/finish')) { + return ok({ + reportDoneResponse: { + responseCode: { _text: '0' }, + reportId: { _text: reportId }, + files: [{ fileId: { _text: fileId } }], + }, + }); + } + if (url === PRESERVATION_URL) { + return ok(undefined); + } + throw new Error(`stub fetchHTTP: unexpected request ${method} ${url}`); + }; + return { fetchHTTP, calls }; +} + +describe('NCMEC submitReport (integration)', () => { + const testWithFixture = makeTransactionalTestWithFixture(async ({ deps }) => { + const orgId = uid(); + const reportId = uid(); + const fileId = 'f1'; + + const orgFixture = await createOrg( + { + KyselyPg: deps.KyselyPg, + ModerationConfigService: deps.ModerationConfigService, + ApiKeyService: deps.ApiKeyService, + }, + orgId, + ); + + await deps.NcmecService.updateNcmecOrgSettings({ + orgId, + username: 'espuser', + password: 'esppass', + contactEmail: 'reporter@example.com', + moreInfoUrl: null, + companyTemplate: 'AcmeESP', + legalUrl: 'https://acme.example/legal', + ncmecPreservationEndpoint: PRESERVATION_URL, + ncmecAdditionalInfoEndpoint: null, + defaultNcmecQueueId: null, + defaultInternetDetailType: 'WEB_PAGE', + termsOfService: null, + contactPersonEmail: null, + contactPersonFirstName: null, + contactPersonLastName: null, + contactPersonPhone: null, + mediaReviewRequirement: 'ALL', + minMediaToReview: null, + }); + + const stub = makeStubFetchHTTP(reportId, fileId); + const ncmecReporting = new NcmecReporting( + deps.KyselyPg, + deps.KyselyPgReadReplica, + stub.fetchHTTP, + deps.SigningKeyPairService, + deps.ModerationConfigService, + deps.getItemTypeEventuallyConsistent, + deps.Tracer, + ); + + return { + orgId, + reportId, + stub, + ncmecReporting, + userItemTypeId: orgFixture.defaultUserItemType.id, + }; + }); + + testWithFixture( + 'submitReport returns SUCCESS, persists a row, and runs submit→upload→fileinfo→finish', + async ({ deps, ncmecReporting, orgId, reportId, stub, userItemTypeId }) => { + const reportedUserId = uid(); + + const reportParams: NCMECReportParams = { + orgId, + reviewerId: 'reviewer-1', + reportedUser: { + id: reportedUserId, + typeId: userItemTypeId, + displayName: 'Jane Doe', + profilePicture: 'https://cdn.example/jane.png', + ipAddress: '203.0.113.7', + email: 'jane@example.com', + }, + media: [ + { + id: 'media-1', + typeId: userItemTypeId, + url: MEDIA_URL, + createdAt: '2026-06-30T12:00:00.000Z', + industryClassification: NCMECIndustryClassification.A1, + fileAnnotations: [NCMECFileAnnotation.GENERATIVE_AI], + hashes: { + md5: 'd41d8cd98f00b204e9800998ecf8427e', + pdq: 'pdqhash', + }, + }, + ], + threads: [], + incidentType: + NCMECIncidentType[ + 'Child Pornography (possession, manufacture, and distribution)' + ], + jobId: 'job-1', + }; + + const result = await ncmecReporting.submitReport(reportParams, false); + expect(result).toBe('SUCCESS'); + + // protocol sequence — the full NCMEC submit flow + const routes = stub.calls + .filter((c) => c.url.includes('cybertip.org')) + .map((c) => c.url.replace(/^.*\/ispws/, '')); + expect(routes).toEqual(['/submit', '/upload', '/fileinfo', '/finish']); + + // preservation fired (isTest=false + endpoint set) + expect(stub.calls.some((c) => c.url === PRESERVATION_URL)).toBe(true); + + // outgoing /submit request shape — proves the field-role-resolved email, + // the incidentType, and the espIdentifier made it into the XML, and that + // #sendCyberTipRequest set a Basic Authorization header. + const submitCall = stub.calls.find( + (c) => c.url.endsWith('/ispws/submit') && typeof c.body === 'string', + ); + if (!submitCall) { + throw new Error( + 'expected a /ispws/submit request with a string body, but none was recorded', + ); + } + const submitXml = String(submitCall.body); + expect(submitXml).toContain(''); + expect(submitXml).toContain('jane@example.com'); + expect(submitCall.headers?.Authorization).toMatch(/^Basic /); + + // persisted row + const row = await deps.KyselyPg.selectFrom( + 'ncmec_reporting.ncmec_reports', + ) + .select(['report_id', 'is_test', 'report_xml']) + .where('org_id', '=', orgId) + .where('report_id', '=', reportId) + .executeTakeFirst(); + expect(row).toBeDefined(); + expect(row?.is_test).toBe(false); + expect(String(row?.report_xml)).toContain('jane@example.com'); + }, + 60_000, + ); +}); From 2bb1e742d8c62ead011024d42f5e65ea06021709 Mon Sep 17 00:00:00 2001 From: Jess Monroe Date: Tue, 7 Jul 2026 17:01:53 +0200 Subject: [PATCH 04/57] fix: Support non-standard scylla ports in DB Migrator (#878) * fix: Support non-standard scylla ports in DB Migrator * better compat * whitespace --- db/src/configs/scylla.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/db/src/configs/scylla.ts b/db/src/configs/scylla.ts index 766132c6..2e0b7e3c 100644 --- a/db/src/configs/scylla.ts +++ b/db/src/configs/scylla.ts @@ -24,6 +24,9 @@ const driverOpts = { consistency: scyllaTypes.consistencies.localQuorum, }, keyspace: process.env.SCYLLA_KEYSPACE!, + protocolOptions: { + port: parseInt(process.env.SCYLLA_PORT ?? '9042'), + }, }; const scyllaEnvironmentDependentOptions = { From c5b0b486fa6997891be79b2f906cf581d4b865c3 Mon Sep 17 00:00:00 2001 From: serendipty01 <34604329+serendipty01@users.noreply.github.com> Date: Tue, 7 Jul 2026 20:51:01 +0530 Subject: [PATCH 05/57] chore(client): migrate test setup to Vitest 4 (#550) --- client/.dockerignore | 1 + client/.eslintrc.cjs | 2 +- client/eslint/__tests__/customRules.test.js | 30 +++++++++++---------- client/package.json | 4 +-- client/tsconfig.json | 9 ++++++- client/tsconfig.test.json | 19 +++++++++++++ client/vite.config.ts | 3 +++ 7 files changed, 50 insertions(+), 18 deletions(-) create mode 100644 client/tsconfig.test.json diff --git a/client/.dockerignore b/client/.dockerignore index 3246d925..e898e62d 100644 --- a/client/.dockerignore +++ b/client/.dockerignore @@ -10,3 +10,4 @@ !tailwind.config.js !tsconfig.json !nginx.conf +!tsconfig.test.json diff --git a/client/.eslintrc.cjs b/client/.eslintrc.cjs index e4993e92..f99eb067 100644 --- a/client/.eslintrc.cjs +++ b/client/.eslintrc.cjs @@ -23,7 +23,7 @@ module.exports = { extends: ['plugin:react/recommended', 'plugin:react-hooks/recommended'], parser: '@typescript-eslint/parser', parserOptions: { - project: ['./tsconfig.json'], + project: ['./tsconfig.json', './tsconfig.test.json'], tsconfigRootDir: __dirname, }, ignorePatterns: [ diff --git a/client/eslint/__tests__/customRules.test.js b/client/eslint/__tests__/customRules.test.js index bdd26c4c..7b8f1bd7 100644 --- a/client/eslint/__tests__/customRules.test.js +++ b/client/eslint/__tests__/customRules.test.js @@ -1,22 +1,24 @@ -const { Linter } = require('eslint'); -const rule = require('../no-casting-in-getFieldValueForRole'); +import tsParser from '@typescript-eslint/parser'; +import { Linter } from 'eslint'; -const linter = new Linter(); +import rule from '../no-casting-in-getFieldValueForRole.js'; + +const linter = new Linter({ configType: 'flat' }); const runLint = (code) => { - const messages = linter.verify(code, { - plugins: { - custom: { - rules: { - 'no-casting-in-getFieldValueForRole': rule, - }, + const messages = linter.verify(code, [ + { + plugins: { + local: { rules: { 'no-casting-in-getFieldValueForRole': rule } }, + }, + rules: { 'local/no-casting-in-getFieldValueForRole': 'error' }, + languageOptions: { + parser: tsParser, + ecmaVersion: 2015, + sourceType: 'module', }, }, - rules: { - 'custom/no-casting-in-getFieldValueForRole': 'error', - }, - languageOptions: { ecmaVersion: 2015, sourceType: 'module' }, - }); + ]); return messages; }; diff --git a/client/package.json b/client/package.json index 504c3df4..3a3f0043 100644 --- a/client/package.json +++ b/client/package.json @@ -7,9 +7,9 @@ "start": "vite --port 3000", "build": "vite build", "test": "vitest --passWithNoTests", - "test:prepush": "vitest --watchAll=false --passWithNoTests", + "test:prepush": "vitest run --passWithNoTests", "check:prepush": "npm run build && npm run test:prepush", - "lint": "tsc --noEmit && eslint ./src", + "lint": "tsc --noEmit && tsc --noEmit -p tsconfig.test.json && eslint ./src", "storybook": "storybook dev -p 6006", "build-storybook": "storybook build", "knip": "knip" diff --git a/client/tsconfig.json b/client/tsconfig.json index 823c15c1..fb0490db 100644 --- a/client/tsconfig.json +++ b/client/tsconfig.json @@ -21,5 +21,12 @@ "@/*": ["./src/*"] } }, - "include": ["src"] + "include": ["src"], + "exclude": [ + "src/**/*.test.ts", + "src/**/*.test.tsx", + "src/**/*.spec.ts", + "src/**/*.spec.tsx", + "src/setupTests.ts" + ] } diff --git a/client/tsconfig.test.json b/client/tsconfig.test.json new file mode 100644 index 00000000..73456acc --- /dev/null +++ b/client/tsconfig.test.json @@ -0,0 +1,19 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "types": [ + "vite/client", + "vite-plugin-svgr/client", + "vitest/globals", + "node" + ] + }, + "include": [ + "src/**/*.test.ts", + "src/**/*.test.tsx", + "src/**/*.spec.ts", + "src/**/*.spec.tsx", + "src/setupTests.ts" + ], + "exclude": ["node_modules"] +} diff --git a/client/vite.config.ts b/client/vite.config.ts index 2e29ca89..ba47330c 100644 --- a/client/vite.config.ts +++ b/client/vite.config.ts @@ -34,5 +34,8 @@ export default defineConfig({ globals: true, environment: 'jsdom', setupFiles: './src/setupTests.ts', + typecheck: { + tsconfig: './tsconfig.test.json', + }, }, }); From 2428fbaef03d2040da76ed89f0552c5bc6743584 Mon Sep 17 00:00:00 2001 From: serendipty01 <34604329+serendipty01@users.noreply.github.com> Date: Thu, 9 Jul 2026 00:03:50 +0530 Subject: [PATCH 06/57] feat: add sepia filter (#62) --- client/src/graphql/generated.ts | 8 ++ client/src/models/safetySettings.test.ts | 77 +++++++++++++++++++ client/src/models/safetySettings.ts | 62 +++++++++++++++ .../mrt/ManualReviewSafetySettings.tsx | 62 +++++++++++---- .../IframeContentDisplayComponent.tsx | 21 ++++- .../ManualReviewJobContentBlurableImage.tsx | 4 +- .../v2/ManualReviewJobFieldsComponent.tsx | 2 + .../v2/ncmec/NCMECMediaViewer.tsx | 48 +++++++++--- .../v2/ncmec/NCMECReviewUser.tsx | 28 ++++--- .../src/webpages/settings/AccountSettings.tsx | 69 ++++++++++++----- .../webpages/settings/SettingsPage.test.tsx | 14 ++-- .../webpages/settings/tabs/WellnessTab.tsx | 54 +++++++++++-- .../2026.07.07T14.27.13.add_sepia.sql | 5 ++ server/graphql/generated.ts | 7 ++ server/graphql/modules/user.ts | 2 + .../modules/JobDecisioning.ts | 5 +- .../services/userManagementService/dbTypes.ts | 6 ++ .../userManagementService.ts | 21 ++++- 18 files changed, 417 insertions(+), 78 deletions(-) create mode 100644 client/src/models/safetySettings.test.ts create mode 100644 client/src/models/safetySettings.ts create mode 100644 db/src/scripts/api-server-pg/2026.07.07T14.27.13.add_sepia.sql diff --git a/client/src/graphql/generated.ts b/client/src/graphql/generated.ts index afc2273b..8ff77a07 100644 --- a/client/src/graphql/generated.ts +++ b/client/src/graphql/generated.ts @@ -2346,6 +2346,7 @@ export type GQLModeratorSafetySettingsInput = { readonly moderatorSafetyBlurLevel: Scalars['Int']['input']; readonly moderatorSafetyGrayscale: Scalars['Boolean']['input']; readonly moderatorSafetyMuteVideo: Scalars['Boolean']['input']; + readonly moderatorSafetySepia: Scalars['Boolean']['input']; }; export const GQLMrtClearReportsDisposition = { @@ -5046,6 +5047,7 @@ export type GQLUserInterfacePreferences = { readonly moderatorSafetyBlurLevel: Scalars['Int']['output']; readonly moderatorSafetyGrayscale: Scalars['Boolean']['output']; readonly moderatorSafetyMuteVideo: Scalars['Boolean']['output']; + readonly moderatorSafetySepia: Scalars['Boolean']['output']; readonly mrtChartConfigurations: ReadonlyArray; }; @@ -12263,6 +12265,7 @@ export type GQLManualReviewSafetySettingsQuery = { readonly moderatorSafetyMuteVideo: boolean; readonly moderatorSafetyGrayscale: boolean; readonly moderatorSafetyBlurLevel: number; + readonly moderatorSafetySepia: boolean; }; } | null; }; @@ -24776,6 +24779,7 @@ export type GQLPersonalSafetySettingsQuery = { readonly moderatorSafetyMuteVideo: boolean; readonly moderatorSafetyGrayscale: boolean; readonly moderatorSafetyBlurLevel: number; + readonly moderatorSafetySepia: boolean; }; } | null; }; @@ -25253,6 +25257,7 @@ export type GQLOrgDefaultSafetySettingsQuery = { readonly moderatorSafetyMuteVideo: boolean; readonly moderatorSafetyGrayscale: boolean; readonly moderatorSafetyBlurLevel: number; + readonly moderatorSafetySepia: boolean; }; } | null; }; @@ -34364,6 +34369,7 @@ export const GQLManualReviewSafetySettingsDocument = gql` moderatorSafetyMuteVideo moderatorSafetyGrayscale moderatorSafetyBlurLevel + moderatorSafetySepia } } } @@ -43008,6 +43014,7 @@ export const GQLPersonalSafetySettingsDocument = gql` moderatorSafetyMuteVideo moderatorSafetyGrayscale moderatorSafetyBlurLevel + moderatorSafetySepia } } } @@ -45198,6 +45205,7 @@ export const GQLOrgDefaultSafetySettingsDocument = gql` moderatorSafetyMuteVideo moderatorSafetyGrayscale moderatorSafetyBlurLevel + moderatorSafetySepia } } } diff --git a/client/src/models/safetySettings.test.ts b/client/src/models/safetySettings.test.ts new file mode 100644 index 00000000..ff049303 --- /dev/null +++ b/client/src/models/safetySettings.test.ts @@ -0,0 +1,77 @@ +import { + colorSchemeClassName, + colorSchemeFromPreferences, + preferencesFromColorScheme, +} from './safetySettings'; + +describe('safetySettings color scheme', () => { + it('maps boolean preferences to a color scheme', () => { + expect( + colorSchemeFromPreferences({ + moderatorSafetyGrayscale: true, + moderatorSafetySepia: false, + }), + ).toBe('GRAYSCALE'); + expect( + colorSchemeFromPreferences({ + moderatorSafetyGrayscale: false, + moderatorSafetySepia: true, + }), + ).toBe('SEPIA'); + expect( + colorSchemeFromPreferences({ + moderatorSafetyGrayscale: false, + moderatorSafetySepia: false, + }), + ).toBe('NONE'); + }); + + it('prefers grayscale if both stored flags are set', () => { + expect( + colorSchemeFromPreferences({ + moderatorSafetyGrayscale: true, + moderatorSafetySepia: true, + }), + ).toBe('GRAYSCALE'); + }); + + it('maps a color scheme back to mutually exclusive booleans', () => { + expect(preferencesFromColorScheme('GRAYSCALE')).toEqual({ + moderatorSafetyGrayscale: true, + moderatorSafetySepia: false, + }); + expect(preferencesFromColorScheme('SEPIA')).toEqual({ + moderatorSafetyGrayscale: false, + moderatorSafetySepia: true, + }); + expect(preferencesFromColorScheme('NONE')).toEqual({ + moderatorSafetyGrayscale: false, + moderatorSafetySepia: false, + }); + }); + + it('round-trips every scheme', () => { + for (const scheme of ['NONE', 'GRAYSCALE', 'SEPIA'] as const) { + expect( + colorSchemeFromPreferences(preferencesFromColorScheme(scheme)), + ).toBe(scheme); + } + }); + + it('maps a color scheme to its Tailwind class', () => { + expect(colorSchemeClassName('GRAYSCALE')).toBe('grayscale'); + expect(colorSchemeClassName('SEPIA')).toBe('sepia'); + expect(colorSchemeClassName('NONE')).toBe(''); + }); + + it('never yields both filter classes even if both flags are set', () => { + expect( + colorSchemeClassName( + colorSchemeFromPreferences({ + moderatorSafetyGrayscale: true, + moderatorSafetySepia: true, + }), + ), + ).toBe('grayscale'); + }); +}); diff --git a/client/src/models/safetySettings.ts b/client/src/models/safetySettings.ts new file mode 100644 index 00000000..71d72669 --- /dev/null +++ b/client/src/models/safetySettings.ts @@ -0,0 +1,62 @@ +export const MODERATOR_SAFETY_COLOR_SCHEMES = [ + 'NONE', + 'GRAYSCALE', + 'SEPIA', +] as const; + +export type ModeratorSafetyColorScheme = + (typeof MODERATOR_SAFETY_COLOR_SCHEMES)[number]; + +export const MODERATOR_SAFETY_COLOR_SCHEME_LABELS: Record< + ModeratorSafetyColorScheme, + string +> = { + NONE: 'None', + GRAYSCALE: 'Grayscale', + SEPIA: 'Sepia', +}; + +// The API stores the color scheme as two independent booleans +// (moderatorSafetyGrayscale / moderatorSafetySepia) so the schema stays +// backwards-compatible; the UI models them as one mutually exclusive scheme. +// Grayscale wins if both flags are somehow set — the UI only ever writes one. +export function colorSchemeFromPreferences(preferences: { + moderatorSafetyGrayscale: boolean; + moderatorSafetySepia: boolean; +}): ModeratorSafetyColorScheme { + if (preferences.moderatorSafetyGrayscale) { + return 'GRAYSCALE'; + } + if (preferences.moderatorSafetySepia) { + return 'SEPIA'; + } + return 'NONE'; +} + +// Tailwind filter class for a resolved color scheme. Deriving classes from the +// resolved scheme (rather than the raw booleans) keeps the "grayscale wins" +// invariant even if both flags are somehow set. +export function colorSchemeClassName( + colorScheme: ModeratorSafetyColorScheme, +): string { + switch (colorScheme) { + case 'GRAYSCALE': + return 'grayscale'; + case 'SEPIA': + return 'sepia'; + case 'NONE': + return ''; + } +} + +export function preferencesFromColorScheme( + colorScheme: ModeratorSafetyColorScheme, +): { + moderatorSafetyGrayscale: boolean; + moderatorSafetySepia: boolean; +} { + return { + moderatorSafetyGrayscale: colorScheme === 'GRAYSCALE', + moderatorSafetySepia: colorScheme === 'SEPIA', + }; +} diff --git a/client/src/webpages/dashboard/mrt/ManualReviewSafetySettings.tsx b/client/src/webpages/dashboard/mrt/ManualReviewSafetySettings.tsx index 4044e57b..b1649c08 100644 --- a/client/src/webpages/dashboard/mrt/ManualReviewSafetySettings.tsx +++ b/client/src/webpages/dashboard/mrt/ManualReviewSafetySettings.tsx @@ -1,4 +1,11 @@ import { Label } from '@/coop-ui/Label'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/coop-ui/Select'; import { Slider } from '@/coop-ui/Slider'; import { Switch } from '@/coop-ui/Switch'; import { gql } from '@apollo/client'; @@ -15,6 +22,14 @@ import { useGQLSetModeratorSafetySettingsMutation, } from '../../../graphql/generated'; import GoldenRetrieverPuppies from '../../../images/GoldenRetrieverPuppies.png'; +import { + colorSchemeClassName, + colorSchemeFromPreferences, + MODERATOR_SAFETY_COLOR_SCHEME_LABELS, + MODERATOR_SAFETY_COLOR_SCHEMES, + preferencesFromColorScheme, + type ModeratorSafetyColorScheme, +} from '../../../models/safetySettings'; import { BLUR_LEVELS, BlurStrength, @@ -27,6 +42,7 @@ gql` moderatorSafetyMuteVideo moderatorSafetyGrayscale moderatorSafetyBlurLevel + moderatorSafetySepia } } } @@ -47,10 +63,12 @@ export default function ManualReviewSafetySettings() { moderatorSafetyBlurLevel: BlurStrength; moderatorSafetyGrayscale: boolean; moderatorSafetyMuteVideo: boolean; + moderatorSafetySepia: boolean; }>({ moderatorSafetyBlurLevel: 2, moderatorSafetyGrayscale: true, moderatorSafetyMuteVideo: true, + moderatorSafetySepia: false, }); const [notificationApi, notificationContextHolder] = notification.useNotification(); @@ -76,11 +94,13 @@ export default function ManualReviewSafetySettings() { moderatorSafetyMuteVideo, moderatorSafetyGrayscale, moderatorSafetyBlurLevel, + moderatorSafetySepia, } = data.me.interfacePreferences; setSettings({ moderatorSafetyMuteVideo, moderatorSafetyGrayscale, moderatorSafetyBlurLevel: moderatorSafetyBlurLevel as BlurStrength, + moderatorSafetySepia, }); }, [data?.me?.interfacePreferences]); @@ -123,27 +143,35 @@ export default function ManualReviewSafetySettings() { step={1} />
-
-
- - setSettings({ - ...settings, - moderatorSafetyGrayscale: value, - }) - } - checked={settings.moderatorSafetyGrayscale} - /> - -
+
+ +
setSettings({ ...settings, @@ -161,7 +189,7 @@ export default function ManualReviewSafetySettings() { settings.moderatorSafetyBlurLevel != null ? BLUR_LEVELS[settings.moderatorSafetyBlurLevel] : 'blur-sm' - } ${settings.moderatorSafetyGrayscale ? 'grayscale' : ''}`} + } ${colorSchemeClassName(colorSchemeFromPreferences(settings))}`} alt="puppies" src={GoldenRetrieverPuppies} /> diff --git a/client/src/webpages/dashboard/mrt/manual_review_job/IframeContentDisplayComponent.tsx b/client/src/webpages/dashboard/mrt/manual_review_job/IframeContentDisplayComponent.tsx index 954849b5..02a72e96 100644 --- a/client/src/webpages/dashboard/mrt/manual_review_job/IframeContentDisplayComponent.tsx +++ b/client/src/webpages/dashboard/mrt/manual_review_job/IframeContentDisplayComponent.tsx @@ -25,25 +25,35 @@ export default function IframeContentDisplayComponent(props: { blur: boolean; grayscale: boolean; shouldTranslate: boolean; + sepia: boolean; }>({ blur: true, grayscale: false, shouldTranslate: false, + sepia: false, }); - const { blur, grayscale, shouldTranslate } = state; + const { blur, grayscale, shouldTranslate, sepia } = state; const { loading, data } = useGQLPersonalSafetySettingsQuery(); - const { moderatorSafetyBlurLevel = 2, moderatorSafetyGrayscale = true } = - data?.me?.interfacePreferences ?? {}; + const { + moderatorSafetyBlurLevel = 2, + moderatorSafetyGrayscale = true, + moderatorSafetySepia = false, + } = data?.me?.interfacePreferences ?? {}; useEffect(() => { setState({ blur: moderatorSafetyBlurLevel !== 0, grayscale: moderatorSafetyGrayscale, shouldTranslate: false, + sepia: moderatorSafetySepia, }); - }, [moderatorSafetyBlurLevel, moderatorSafetyGrayscale]); + }, [ + moderatorSafetyBlurLevel, + moderatorSafetyGrayscale, + moderatorSafetySepia, + ]); useEffect(() => { // Translation status messages come from the content proxy. With no proxy @@ -77,6 +87,7 @@ export default function IframeContentDisplayComponent(props: { blur: blur ? moderatorSafetyBlurLevel : 0, grayscale, shouldTranslate, + sepia, }, proxyUrl, ); @@ -93,6 +104,7 @@ export default function IframeContentDisplayComponent(props: { blur: blur ? moderatorSafetyBlurLevel : 0, grayscale, shouldTranslate, + sepia, }, proxyUrl, ); @@ -116,6 +128,7 @@ export default function IframeContentDisplayComponent(props: { shouldTranslate, contentProxyUrl, isIframeLoading, + sepia, ], ); diff --git a/client/src/webpages/dashboard/mrt/manual_review_job/ManualReviewJobContentBlurableImage.tsx b/client/src/webpages/dashboard/mrt/manual_review_job/ManualReviewJobContentBlurableImage.tsx index 973a9db1..32f58cfe 100644 --- a/client/src/webpages/dashboard/mrt/manual_review_job/ManualReviewJobContentBlurableImage.tsx +++ b/client/src/webpages/dashboard/mrt/manual_review_job/ManualReviewJobContentBlurableImage.tsx @@ -14,6 +14,7 @@ export default function ManualReviewJobContentBlurableImage(props: { blurStrength?: BlurStrength; grayscale?: boolean; disableZoom?: boolean; + sepia?: boolean; }; onError?: () => void; }) { @@ -25,6 +26,7 @@ export default function ManualReviewJobContentBlurableImage(props: { blurStrength = 0, grayscale = false, disableZoom = false, + sepia = false, } = options ?? {}; const [clicked, setClicked] = useState(false); @@ -49,7 +51,7 @@ export default function ManualReviewJobContentBlurableImage(props: { setClicked(true)} diff --git a/client/src/webpages/dashboard/mrt/manual_review_job/v2/ManualReviewJobFieldsComponent.tsx b/client/src/webpages/dashboard/mrt/manual_review_job/v2/ManualReviewJobFieldsComponent.tsx index 3cf58a53..347011ac 100644 --- a/client/src/webpages/dashboard/mrt/manual_review_job/v2/ManualReviewJobFieldsComponent.tsx +++ b/client/src/webpages/dashboard/mrt/manual_review_job/v2/ManualReviewJobFieldsComponent.tsx @@ -278,6 +278,7 @@ function TableRowComponent(props: { ? (safetySettings.moderatorSafetyBlurLevel as BlurStrength) : (2 as const), grayscale: safetySettings?.moderatorSafetyGrayscale ?? false, + sepia: safetySettings?.moderatorSafetySepia ?? false, }} /> {label ?
{label}
: null} @@ -352,6 +353,7 @@ function TableRowComponent(props: { ? (safetySettings.moderatorSafetyBlurLevel as BlurStrength) : (2 as const), grayscale: safetySettings?.moderatorSafetyGrayscale ?? false, + sepia: safetySettings?.moderatorSafetySepia ?? false, }} /> {label ?
{label}
: null} diff --git a/client/src/webpages/dashboard/mrt/manual_review_job/v2/ncmec/NCMECMediaViewer.tsx b/client/src/webpages/dashboard/mrt/manual_review_job/v2/ncmec/NCMECMediaViewer.tsx index 5ea0d96d..c24b7b25 100644 --- a/client/src/webpages/dashboard/mrt/manual_review_job/v2/ncmec/NCMECMediaViewer.tsx +++ b/client/src/webpages/dashboard/mrt/manual_review_job/v2/ncmec/NCMECMediaViewer.tsx @@ -1,6 +1,19 @@ import { Label } from '@/coop-ui/Label'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/coop-ui/Select'; import { Slider } from '@/coop-ui/Slider'; -import { Switch } from '@/coop-ui/Switch'; +import { + colorSchemeFromPreferences, + MODERATOR_SAFETY_COLOR_SCHEME_LABELS, + MODERATOR_SAFETY_COLOR_SCHEMES, + preferencesFromColorScheme, + type ModeratorSafetyColorScheme, +} from '@/models/safetySettings'; import { SearchOutlined } from '@ant-design/icons'; import { useEffect, useState } from 'react'; @@ -84,10 +97,12 @@ export default function NCMECMediaViewer(props: { moderatorSafetyBlurLevel: BlurStrength; moderatorSafetyGrayscale: boolean; moderatorSafetyMuteVideo: boolean; + moderatorSafetySepia: boolean; }>({ moderatorSafetyBlurLevel: 2, moderatorSafetyGrayscale: true, moderatorSafetyMuteVideo: true, + moderatorSafetySepia: false, }); const { loading, error, data } = useGQLPersonalSafetySettingsQuery(); @@ -170,7 +185,7 @@ export default function NCMECMediaViewer(props: { shouldBlur ? BLUR_LEVELS[safetySettings.moderatorSafetyBlurLevel] : 0 - } ${safetySettings.moderatorSafetyGrayscale ? 'grayscale' : ''}`} + } ${safetySettings.moderatorSafetyGrayscale ? 'grayscale' : ''} ${safetySettings.moderatorSafetySepia ? 'sepia' : ''}`} alt="" src={mediaId.urlInfo.url} onError={(img) => { @@ -190,7 +205,7 @@ export default function NCMECMediaViewer(props: { isInInspectedView ? 'w-auto' : 'object-scale-down grow max-w-64 max-h-48' - } ${safetySettings.moderatorSafetyGrayscale ? 'grayscale' : ''}`} + } ${safetySettings.moderatorSafetyGrayscale ? 'grayscale' : ''} ${safetySettings.moderatorSafetySepia ? 'sepia' : ''}`} url={mediaId.urlInfo.url} options={{ shouldBlur: @@ -254,18 +269,29 @@ export default function NCMECMediaViewer(props: { />
- + +
)} diff --git a/client/src/webpages/dashboard/mrt/manual_review_job/v2/ncmec/NCMECReviewUser.tsx b/client/src/webpages/dashboard/mrt/manual_review_job/v2/ncmec/NCMECReviewUser.tsx index 7300d318..037a4316 100644 --- a/client/src/webpages/dashboard/mrt/manual_review_job/v2/ncmec/NCMECReviewUser.tsx +++ b/client/src/webpages/dashboard/mrt/manual_review_job/v2/ncmec/NCMECReviewUser.tsx @@ -3,6 +3,7 @@ import { BulbOutlined, ExclamationCircleOutlined } from '@ant-design/icons'; import { gql } from '@apollo/client'; import { ItemIdentifier, MediaKind, TaggedScalar } from '@roostorg/coop-types'; import { Button } from 'antd'; +import clsx from 'clsx'; import pick from 'lodash/pick'; import uniqBy from 'lodash/uniqBy'; import uniqWith from 'lodash/uniqWith'; @@ -446,9 +447,10 @@ export default function NCMECReviewUser( } const { - moderatorSafetyBlurLevel, - moderatorSafetyGrayscale, - moderatorSafetyMuteVideo, + moderatorSafetyBlurLevel = 2 as BlurStrength, + moderatorSafetyGrayscale = true, + moderatorSafetyMuteVideo = true, + moderatorSafetySepia = false, } = data?.me?.interfacePreferences ?? {}; // Compares two pieces of media to determine whether they're the same, based @@ -634,23 +636,29 @@ export default function NCMECReviewUser(
{!loading && moderatorSafetyBlurLevel != null && - moderatorSafetyGrayscale != null ? ( + moderatorSafetyGrayscale != null && + moderatorSafetySepia != null ? ( media.urlInfo.mediaType === 'IMAGE' ? ( ) : ( ({ @@ -228,12 +246,14 @@ export default function AccountSettings() { moderatorSafetyMuteVideo, moderatorSafetyGrayscale, moderatorSafetyBlurLevel, + moderatorSafetySepia, } = safetySettingsData.me.interfacePreferences; setSafetySettings({ moderatorSafetyMuteVideo, moderatorSafetyGrayscale, moderatorSafetyBlurLevel: moderatorSafetyBlurLevel as BlurStrength, + moderatorSafetySepia, }); }, [safetySettingsData?.me?.interfacePreferences]); @@ -298,11 +318,20 @@ export default function AccountSettings() { })); }, []); - const setGrayscalePreference = useCallback( - (moderatorSafetyGrayscale: boolean): void => + const setMuteVideoPreference = useCallback( + (moderatorSafetyMuteVideo: boolean): void => + setSafetySettings((prevSettings) => ({ + ...prevSettings, + moderatorSafetyMuteVideo, + })), + [], + ); + + const setColorSchemePreference = useCallback( + (colorScheme: ModeratorSafetyColorScheme): void => setSafetySettings((prevSettings) => ({ ...prevSettings, - moderatorSafetyGrayscale, + ...preferencesFromColorScheme(colorScheme), })), [], ); @@ -366,15 +395,6 @@ export default function AccountSettings() { }); }, [currentPassword, newPassword, confirmNewPassword, changePassword]); - const setMuteVideoPreference = useCallback( - (moderatorSafetyMuteVideo: boolean): void => - setSafetySettings((prevSettings) => ({ - ...prevSettings, - moderatorSafetyMuteVideo, - })), - [], - ); - const moderatorSafetyBlurValue = useMemo( () => [safetySettings.moderatorSafetyBlurLevel], [safetySettings.moderatorSafetyBlurLevel], @@ -589,11 +609,24 @@ export default function AccountSettings() { />
- - + +
@@ -608,7 +641,7 @@ export default function AccountSettings() { puppies diff --git a/client/src/webpages/settings/SettingsPage.test.tsx b/client/src/webpages/settings/SettingsPage.test.tsx index e21ac462..b0796e05 100644 --- a/client/src/webpages/settings/SettingsPage.test.tsx +++ b/client/src/webpages/settings/SettingsPage.test.tsx @@ -128,6 +128,7 @@ const wellnessSettingsMock: MockedResponse = { moderatorSafetyBlurLevel: 2, moderatorSafetyGrayscale: true, moderatorSafetyMuteVideo: true, + moderatorSafetySepia: false, }, }, }, @@ -562,12 +563,14 @@ describe('SettingsPage', () => { screen.getByText('Default Wellness Settings'), ).toBeInTheDocument(); expect(screen.getByText('Blur Media')).toBeInTheDocument(); - expect(screen.getByText('Greyscale')).toBeInTheDocument(); + expect(screen.getByText('Color Scheme')).toBeInTheDocument(); expect(screen.getByText('Mute videos')).toBeInTheDocument(); }); + // Grayscale=true / sepia=false in the mock maps to the GRAYSCALE scheme. + expect(screen.getByRole('combobox')).toHaveTextContent('Grayscale'); const switches = screen.getAllByRole('switch'); + expect(switches).toHaveLength(1); expect(switches[0]).toHaveAttribute('aria-checked', 'true'); - expect(switches[1]).toHaveAttribute('aria-checked', 'true'); }); it('calls save mutation with updated settings', async () => { @@ -584,8 +587,9 @@ describe('SettingsPage', () => { variables: { orgDefaultSafetySettings: { moderatorSafetyBlurLevel: 2, - moderatorSafetyGrayscale: false, - moderatorSafetyMuteVideo: true, + moderatorSafetyGrayscale: true, + moderatorSafetyMuteVideo: false, + moderatorSafetySepia: false, }, }, }, @@ -595,7 +599,7 @@ describe('SettingsPage', () => { 'wellness', ); await waitFor(() => { - expect(screen.getByText('Greyscale')).toBeInTheDocument(); + expect(screen.getByText('Mute videos')).toBeInTheDocument(); }); userEvent.click(screen.getAllByRole('switch')[0]); diff --git a/client/src/webpages/settings/tabs/WellnessTab.tsx b/client/src/webpages/settings/tabs/WellnessTab.tsx index fe4c6504..dc738f5f 100644 --- a/client/src/webpages/settings/tabs/WellnessTab.tsx +++ b/client/src/webpages/settings/tabs/WellnessTab.tsx @@ -1,13 +1,29 @@ import { Button } from '@/coop-ui/Button'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/coop-ui/Select'; import { Slider } from '@/coop-ui/Slider'; import { Switch } from '@/coop-ui/Switch'; import { toast } from '@/coop-ui/Toast'; import { Heading, Text } from '@/coop-ui/Typography'; import { + namedOperations, useGQLOrgDefaultSafetySettingsQuery, useGQLSetOrgDefaultSafetySettingsMutation, } from '@/graphql/generated'; import GoldenRetrieverPuppies from '@/images/GoldenRetrieverPuppies.png'; +import { + colorSchemeClassName, + colorSchemeFromPreferences, + MODERATOR_SAFETY_COLOR_SCHEME_LABELS, + MODERATOR_SAFETY_COLOR_SCHEMES, + preferencesFromColorScheme, + type ModeratorSafetyColorScheme, +} from '@/models/safetySettings'; import { gql } from '@apollo/client'; import { useEffect, useState } from 'react'; @@ -28,6 +44,7 @@ gql` moderatorSafetyMuteVideo moderatorSafetyGrayscale moderatorSafetyBlurLevel + moderatorSafetySepia } } } @@ -47,6 +64,7 @@ type SafetySettings = { moderatorSafetyBlurLevel: BlurStrength; moderatorSafetyGrayscale: boolean; moderatorSafetyMuteVideo: boolean; + moderatorSafetySepia: boolean; }; export default function WellnessTab() { @@ -54,6 +72,7 @@ export default function WellnessTab() { moderatorSafetyBlurLevel: 2, moderatorSafetyGrayscale: true, moderatorSafetyMuteVideo: true, + moderatorSafetySepia: false, }); const { loading, error, data } = useGQLOrgDefaultSafetySettingsQuery({ @@ -64,6 +83,9 @@ export default function WellnessTab() { const [saveSafetySettings, { loading: isSaving }] = useGQLSetOrgDefaultSafetySettingsMutation({ + // The mutation returns no data, so refetch to update the cached + // baseline that hasChanges compares against. + refetchQueries: [namedOperations.Query.OrgDefaultSafetySettings], onCompleted: () => { toast.success('Default wellness settings saved!'); }, @@ -80,11 +102,13 @@ export default function WellnessTab() { moderatorSafetyMuteVideo, moderatorSafetyGrayscale, moderatorSafetyBlurLevel, + moderatorSafetySepia, } = defaultInterfacePreferences; setSafetySettings({ moderatorSafetyMuteVideo, moderatorSafetyGrayscale, moderatorSafetyBlurLevel: moderatorSafetyBlurLevel as BlurStrength, + moderatorSafetySepia, }); }, [defaultInterfacePreferences]); @@ -99,7 +123,8 @@ export default function WellnessTab() { safetySettings.moderatorSafetyGrayscale !== serverPrefs.moderatorSafetyGrayscale || safetySettings.moderatorSafetyMuteVideo !== - serverPrefs.moderatorSafetyMuteVideo; + serverPrefs.moderatorSafetyMuteVideo || + safetySettings.moderatorSafetySepia !== serverPrefs.moderatorSafetySepia; return (
@@ -139,17 +164,30 @@ export default function WellnessTab() {
- Greyscale + Color Scheme - +
@@ -169,7 +207,7 @@ export default function WellnessTab() { puppies diff --git a/db/src/scripts/api-server-pg/2026.07.07T14.27.13.add_sepia.sql b/db/src/scripts/api-server-pg/2026.07.07T14.27.13.add_sepia.sql new file mode 100644 index 00000000..51c83597 --- /dev/null +++ b/db/src/scripts/api-server-pg/2026.07.07T14.27.13.add_sepia.sql @@ -0,0 +1,5 @@ +ALTER TABLE user_management_service.user_interface_settings +ADD COLUMN moderator_safety_sepia boolean; + +ALTER TABLE user_management_service.org_default_user_interface_settings +ADD COLUMN moderator_safety_sepia boolean NOT NULL DEFAULT false; diff --git a/server/graphql/generated.ts b/server/graphql/generated.ts index 4920888e..8430dcfd 100644 --- a/server/graphql/generated.ts +++ b/server/graphql/generated.ts @@ -2414,6 +2414,7 @@ export type GQLModeratorSafetySettingsInput = { readonly moderatorSafetyBlurLevel: Scalars['Int']['input']; readonly moderatorSafetyGrayscale: Scalars['Boolean']['input']; readonly moderatorSafetyMuteVideo: Scalars['Boolean']['input']; + readonly moderatorSafetySepia: Scalars['Boolean']['input']; }; export const GQLMrtClearReportsDisposition = { @@ -5114,6 +5115,7 @@ export type GQLUserInterfacePreferences = { readonly moderatorSafetyBlurLevel: Scalars['Int']['output']; readonly moderatorSafetyGrayscale: Scalars['Boolean']['output']; readonly moderatorSafetyMuteVideo: Scalars['Boolean']['output']; + readonly moderatorSafetySepia: Scalars['Boolean']['output']; readonly mrtChartConfigurations: ReadonlyArray; }; @@ -14717,6 +14719,11 @@ export type GQLUserInterfacePreferencesResolvers< ParentType, ContextType >; + moderatorSafetySepia?: Resolver< + GQLResolversTypes['Boolean'], + ParentType, + ContextType + >; mrtChartConfigurations?: Resolver< ReadonlyArray, ParentType, diff --git a/server/graphql/modules/user.ts b/server/graphql/modules/user.ts index 45d9febd..edd51e9d 100644 --- a/server/graphql/modules/user.ts +++ b/server/graphql/modules/user.ts @@ -79,6 +79,7 @@ const typeDefs = /* GraphQL */ ` type UserInterfacePreferences { moderatorSafetyMuteVideo: Boolean! moderatorSafetyGrayscale: Boolean! + moderatorSafetySepia: Boolean! moderatorSafetyBlurLevel: Int! mrtChartConfigurations: [ManualReviewChartSettings!]! } @@ -87,6 +88,7 @@ const typeDefs = /* GraphQL */ ` moderatorSafetyMuteVideo: Boolean! moderatorSafetyGrayscale: Boolean! moderatorSafetyBlurLevel: Int! + moderatorSafetySepia: Boolean! } input ManualReviewChartConfigurationsInput { diff --git a/server/services/manualReviewToolService/modules/JobDecisioning.ts b/server/services/manualReviewToolService/modules/JobDecisioning.ts index af7edccf..1be10fec 100644 --- a/server/services/manualReviewToolService/modules/JobDecisioning.ts +++ b/server/services/manualReviewToolService/modules/JobDecisioning.ts @@ -413,7 +413,10 @@ export default class JobDecisioning { return { newDecisionStored: logDecisionStatus === 'SUCCESS', - error: match([logDecisionStatus, removeJobStatus] as const) + error: match([logDecisionStatus, removeJobStatus] as readonly [ + 'SUCCESS' | 'ALREADY_LOGGED', + 'SUCCESS' | 'FAILED', + ]) // Case 1, happy path. .with(['SUCCESS', 'SUCCESS'], () => undefined) // Case 2, decision logged but job not deleted. diff --git a/server/services/userManagementService/dbTypes.ts b/server/services/userManagementService/dbTypes.ts index 707d3130..97753f36 100644 --- a/server/services/userManagementService/dbTypes.ts +++ b/server/services/userManagementService/dbTypes.ts @@ -27,6 +27,7 @@ export type UserManagementPg = { user_id: string; moderator_safety_mute_video: boolean | null; moderator_safety_grayscale: boolean | null; + moderator_safety_sepia: boolean | null; moderator_safety_blur_level: number | null; mrt_chart_configurations: MrtChartConfig[] | null; }; @@ -45,6 +46,11 @@ export type UserManagementPg = { boolean | undefined, boolean | undefined >; + moderator_safety_sepia: ColumnType< + boolean, + boolean | undefined, + boolean | undefined + >; moderator_safety_blur_level: ColumnType< number, number | undefined, diff --git a/server/services/userManagementService/userManagementService.ts b/server/services/userManagementService/userManagementService.ts index b04b6e2e..fa617c84 100644 --- a/server/services/userManagementService/userManagementService.ts +++ b/server/services/userManagementService/userManagementService.ts @@ -1,3 +1,4 @@ +/* eslint-disable max-lines */ import crypto from 'node:crypto'; import { type Kysely } from 'kysely'; @@ -54,15 +55,17 @@ class UserManagementService { if ( row && - row.moderator_safety_grayscale && - row.moderator_safety_blur_level && - row.moderator_safety_mute_video + row.moderator_safety_grayscale != null && + row.moderator_safety_blur_level != null && + row.moderator_safety_mute_video != null && + row.moderator_safety_sepia != null ) { // If all the user's settings have been set, just return them return { moderatorSafetyGrayscale: row.moderator_safety_grayscale, moderatorSafetyBlurLevel: row.moderator_safety_blur_level, moderatorSafetyMuteVideo: row.moderator_safety_mute_video, + moderatorSafetySepia: row.moderator_safety_sepia, mrtChartConfigurations: row.mrt_chart_configurations ?? [], }; } @@ -73,6 +76,8 @@ class UserManagementService { return { moderatorSafetyGrayscale: row?.moderator_safety_grayscale ?? orgDefaults.moderatorSafetyGrayscale, + moderatorSafetySepia: + row?.moderator_safety_sepia ?? orgDefaults.moderatorSafetySepia, moderatorSafetyBlurLevel: row?.moderator_safety_blur_level ?? orgDefaults.moderatorSafetyBlurLevel, @@ -163,6 +168,7 @@ class UserManagementService { moderatorSafetyMuteVideo: boolean; moderatorSafetyGrayscale: boolean; moderatorSafetyBlurLevel: number; + moderatorSafetySepia: boolean; }; mrtChartConfigurations?: readonly MrtChartConfig[]; }; @@ -176,6 +182,8 @@ class UserManagementService { ? { moderator_safety_grayscale: moderatorSafetySettings.moderatorSafetyGrayscale, + moderator_safety_sepia: + moderatorSafetySettings.moderatorSafetySepia, moderator_safety_blur_level: moderatorSafetySettings.moderatorSafetyBlurLevel, moderator_safety_mute_video: @@ -221,6 +229,7 @@ class UserManagementService { return { moderatorSafetyGrayscale: row.moderator_safety_grayscale, + moderatorSafetySepia: row.moderator_safety_sepia, moderatorSafetyBlurLevel: row.moderator_safety_blur_level, moderatorSafetyMuteVideo: row.moderator_safety_mute_video, }; @@ -231,12 +240,14 @@ class UserManagementService { // If you don't provide these values, they will be set to the default values // configured on the pg table definition moderatorSafetyGrayscale?: boolean; + moderatorSafetySepia?: boolean; moderatorSafetyBlurLevel?: number; moderatorSafetyMuteVideo?: boolean; }) { const { orgId, moderatorSafetyGrayscale, + moderatorSafetySepia, moderatorSafetyBlurLevel, moderatorSafetyMuteVideo, } = opts; @@ -244,6 +255,9 @@ class UserManagementService { ...(moderatorSafetyGrayscale !== undefined ? { moderator_safety_grayscale: moderatorSafetyGrayscale } : {}), + ...(moderatorSafetySepia !== undefined + ? { moderator_safety_sepia: moderatorSafetySepia } + : {}), ...(moderatorSafetyBlurLevel !== undefined ? { moderator_safety_blur_level: moderatorSafetyBlurLevel } : {}), @@ -258,6 +272,7 @@ class UserManagementService { { org_id: orgId, moderator_safety_grayscale: moderatorSafetyGrayscale, + moderator_safety_sepia: moderatorSafetySepia, moderator_safety_blur_level: moderatorSafetyBlurLevel, moderator_safety_mute_video: moderatorSafetyMuteVideo, }, From ee8eafe5c9cc266ceb1fbfd5f3883c1fca9feadb Mon Sep 17 00:00:00 2001 From: Caleb McQuaid Date: Wed, 8 Jul 2026 14:36:54 -0400 Subject: [PATCH 07/57] chore: changelog update --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 69f1c51b..fe6779a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ **Full Changelog**: https://github.com/roostorg/coop/compare/1.0.2...main +## Review Console + +- Added per-queue job sort modes for manual review: FIFO (default, unchanged), most-reported-first, and custom weighted — ordering is computed at enqueue via BullMQ job priority, so the locked dequeue path is unchanged (#718) +- Added org-configurable job priority weights (Settings → Job Priorities) for weighted queues; changing weights or a queue's sort mode re-sorts already-queued jobs in a background sweep (#892) +- Skip is now per-reviewer: a skipped job is hidden from that reviewer for 30 minutes while returning to the shared pool immediately; entering a drained or fully-skipped queue redirects to the queue list (#893) + # Coop 1.0.2 This release addresses reported security advisories, improves NCMEC CyberTipline reporting, and includes front-end quality-of-life improvements. From 150c48bf1e84008f85827cf87f8cfef656b695a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tao=20Bojl=C3=A9n?= Date: Mon, 13 Jul 2026 09:28:14 +0100 Subject: [PATCH 08/57] Add Playwright E2E coverage for moderator flows (#875) --- .github/workflows/e2e.yaml | 3 +- client/public/e2e/tone-3s.wav | Bin 0 -> 264678 bytes server/.gitignore | 4 + server/e2e/fixtures/coop.ts | 186 +++++++++++++++++++- server/e2e/fixtures/media.ts | 11 ++ server/e2e/playwright.config.ts | 9 +- server/e2e/tests/investigation.spec.ts | 38 ++++ server/e2e/tests/item-signals.spec.ts | 60 +++++++ server/e2e/tests/item-type-creation.spec.ts | 48 +++++ server/e2e/tests/login.spec.ts | 18 +- server/e2e/tests/mrt-job-review.spec.ts | 111 ++++++++++++ server/e2e/tests/rule-creation.spec.ts | 72 ++++++++ server/e2e/tests/rule-routing.spec.ts | 67 +++++++ server/utils/encoding.ts | 2 +- 14 files changed, 620 insertions(+), 9 deletions(-) create mode 100644 client/public/e2e/tone-3s.wav create mode 100644 server/e2e/fixtures/media.ts create mode 100644 server/e2e/tests/investigation.spec.ts create mode 100644 server/e2e/tests/item-signals.spec.ts create mode 100644 server/e2e/tests/item-type-creation.spec.ts create mode 100644 server/e2e/tests/mrt-job-review.spec.ts create mode 100644 server/e2e/tests/rule-creation.spec.ts create mode 100644 server/e2e/tests/rule-routing.spec.ts diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml index 0daab5af..5211d630 100644 --- a/.github/workflows/e2e.yaml +++ b/.github/workflows/e2e.yaml @@ -82,6 +82,7 @@ jobs: cp server/.env.example server/.env cp db/.env.example db/.env cp client/.env.example client/.env + sed -i "s/^ITEM_QUEUE_TRAFFIC_PERCENTAGE=.*/ITEM_QUEUE_TRAFFIC_PERCENTAGE='1'/" server/.env - name: Install dependencies run: | @@ -144,7 +145,7 @@ jobs: PLAYWRIGHT_BASE_URL: http://localhost:3000 - name: Upload Playwright report - if: ${{ !cancelled() }} + if: ${{ failure() }} uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: playwright-report diff --git a/client/public/e2e/tone-3s.wav b/client/public/e2e/tone-3s.wav new file mode 100644 index 0000000000000000000000000000000000000000..755a5c30bb24be30e24772ba3c019cadbaa89a20 GIT binary patch literal 264678 zcmeI*iMLK=-v{u04}0%>PuFEurVgP_h7itUNXJY_hD;ffQyrx!M9L5{gePOJNG9sX{)t}{PfFY5d5I^Ldg?#wd)g!VE&VkkG{%y1 z)hZF9#91JOZ8RlUj3Lp$hd4gPL7Z~I)ZMbKT(rrX^Q?u57Pzo5t<^)$$jKQ z<1S-{eo?EZjZ!x%XA>E@vfM~&BX*1Tj`fZ9i}VxvhJCaBGlSAkrCv%-4_5m7ysK_S zx0mydebee}?J(<_AM+0UI$Oa8vHMwR7G-f(o)xg+Y!ged$N4ed!TioFwhVi}J;5n- zOL^D56aJ21ZE|U9etK?ZPIgW>SC|`_6P+2G93L$Xl{(A!C8Emr%2KtL7S-R^TN+;( zt;tqm(1COXJxTwjncVd^{f4fhgDIukNjq}PC^Rc{i7?B*3>YMJB>5=Ub_7r+V z9*uU3b&R(Z>qzBgU;aMvo-#ts*Uo8E^~%NvMs2c`1f(l{i+-8=BvQF2%cN)MCi()c zL64HbhjsxmqEWNEB6dz8&#KeBp!3D?Z|W)170 zHNw8zx#DbdXLv<^=b%aQo>aB;otX;R3SmW|Vx(fUT#Up;(G@RAN8~k$vC2bgsP5DT z>484m(8y$Rg*2zH(>?TJZgqszqrcN#bRum+e<$Nfj7%|@-c$cTv(!fFGs>dGZuzuy zRdnLnSUe_0<%lFmp_ENzNV;OGRECHpwIS-;%HhOyS(2+rb;W}CgR#PB>qwzc7#3#RWICj~rv@fR z2DAK)-uJHPwsvOQ=d9+|`(~cGfj`JEu_dfOtIKHaxf0nOtOa|4ZD%oF#DC;H&EL&& zRu%hOdydo7t?6aGi~h0T)8v-ahV;73+U&Y;ov<#lCb}}VBtA=gSsE<2N>os;C>zyb zS{Z$(-r4xx=t}mG@^lzoL%*XXxs{%#H|cTu4(&%}`T=Q1_8AWt|It0Iqc%r9qBw~v ziRN-oX_z=N{&MV<=&O-eg;&D~+1D~t(lb+wlJ5nd`{%r<_kjDnv)zu`gRQU4R^}JH zH+R^2_7rQv%CT7PD#IGFq3m5&!n*QLd1G^**~@aR_4W{_rfaz;yv_dNU~2N^)br`# znc{45SS$>W6i16J}rR`QtrKZv?@q#>4S}tygZ;kDY?uzUZc7;2$yE1#y z2U5qAzXr@V{0F?D?mNyoJKvsaT{9my&-3TF!gsTmSSwa3_uh>#gXOcw*nD=LHRY>$ zMRSGO!1~U5&2H>?&S7`CH{Kr{bV)v#YLae{sh_PM))(%N)Q{GQ)rwaZHOZBJk#{C$ zDm~Ov+F@;kE*ndX3S>69MOxFDbRWG;gWQwl&~tP<9Y^capU4ZuGe#SK=xz07+9fql zeN35>SS#<7z7sFRuf_h3mPAT~lJK8wNrtDrR3ud@Su1Gm7kTsDuN={N)ZSpJ)&$e% z(|I+1lucosS#_r5?nB9aeV9#U$56DkY zGj`Sv9l?(9=&ji0_)p>?>3w-xqMuSz{Zn0`b=3dVUpA7)C~}tEPsh`bbN_!QcOL=$ zgMLompoR1XnL)~uMMm5ht{>ENt(!Vs*_QZ8=8_~;5cA@-WBJki$bCY7n4hhaX^?J~ z>W~}|jPaLv2VBc(;EcCVT6L}EW;t^uZ_IyXZ?WF&-rQ$#doQfS9%5tI9;Wf<`2{}M zylK8+)wM6!E1jp@0Ajgxv!8~0g}sqI(Vel)@zvs7X|&ukk*|2l zXX+R&Pya&iWBg|HA%{p+I-I_rd!G1zSIBkx63&XqJFGg zO2~;?a!aY3*gyV6Y*@4?QX~uuhh>X0!_%Wu6O(TR8~m@m61TcL&{<9`XN@Q~M)!3MLvDjC7NX|cd)6TctNO4jL`VCZf>(bU8giZ{>Ev zEd7U`qVLk7v@G3C9ww)Z4#sw!=tH#i>P4lD(mXLheo2}xE{HFSt%$CQtP)m+E3>OI zYtkR2b|jAk=l!IY=XG~yIw$Nq?J?Flv%7hkKglC}D|?bPj)&*;>-QH2$Gwv2|oE= zOFOL+wYBnGVxGJ~+AAK7pN^f0{v7#PI2)eL{+v0NzLdI|Oa`Tby8ffyM0bZ{+lBTr zD{a1D{>>-w3j7dzm9@+59sj!*)?|;cnd~&n=kvK}&NeGspISwBCFh*8!F|o^@3#u> zOIAshO_Pk4RYOhCBU)66#o|Hyrg%pBOkSE8p)^%Jb+h)EZtBwvkxU>LNdcWichd{G zeem{Pc!7RGr_g5f4>FM`WQO7D1N3cLN^POOsH{vJl+R0C4C88?#>zy?M#>1K!!p@& znLE=pQVo*rf+7Cv-e&irbC*+W@3Zc*=9!AQh}Ypiu-U9TtCic&Be{F2%G$D5*nU=) zkK;G^vu0o}wp!ZE-s+5ZyLk8bN?-?9lE0>YOrOqtmpvVx7EVV_MNh=Oi0=~DNVDXp z6D<`*J)ut18tC8Y!;I_36XZClNk`HT>Dk<8aeFVkO2450rVmq(EFm??dgBgblK!(+ zOM6CLt$drXEB{7H{RwjsKyKQ;lvW50Nj8X2v=_)Vgbn)o+z-qIROaJWzT;oDhFK_C|C@ zWQOoYI3qhVGbg<;wJNza_}c%|)4i7NSZA-T*iTvCm~G9kd4C?TjjWh8&7F5|pB78A z25bmh$!@Yvd?&AO?l!wx|5z*RK2Bx#hI`0cgm(*fM`}d#VwK{h#E5iLIwgOYn51-4W$kmVNEeK^ zj8bGexkg&i>GZSQ&r!Eei!MD+chCv6A^nAnCK+Rbab54CzpLF)^VPx1n~BZx*U~xh zM%;?|(cJbYq{3u2naQN1X+2db*&yiRzu+x*PdUWtXMbdswx*a_KAYFzC)hOBh1K}K z(_$X$$R@L|SQY*{clbC{vesE$?TB;GdD9*2HTUld)MP5jQ#aGsGS{-#!t28I$kphj z*w69f;%;e$JR#9tsjU8{F4i8>f7eGDw(%VKfz+jA={EZ7e@}}6y+ZfXS+p%JA#=!` z(Btp0YP_K~77hrRrjxc*9uZXp=}2p>fzaTaamyZkOtjED9$1tG#1x z+G*xYv46B4u--K*nQM77{ySU9`mp;L$*pve-N_zgFS4CX=EM0fyq|f+oMhFs&)AEd zfo?r7;otCo3=SthO?{l+n)xXEQTUPYQDjT>gV?+A#p3HyvD_(fm-3JDvHF~Lr~aA# zsBzZlK|UvU(x>Tr^!wcT>h@{z20cpOroCy5t|N`fCq`Xkv0kFJ(x$4PDK`_P6Ak2! zQa^E6{Mp#Z=%~mjVPrTmJ1R3K{aR{P@||G2f7%P&e7DHiXeX_{)*-XN{G4~^oULU~ zvIkh%|L%olSz}hj)-j9q;QM)V^GkDpm9e(i#ZG-UTSv$tkH<(ql8Dv!lZ? z!kEbD=&0E6_#m;X)I_e3C`lYpW~gm6Q(LOvZ|pS+$a*5uK6EKPmb-)7r^OO_ny#Zy z(Ta30=}vw!x*2=)3VN~jk$PRJth7xO$rGiw#1--PVjn~|MK%c=!w<3>Gh5O-QU{W! zfaf0SF}uK;^Y?gFbDddW zowcUh4?3aqjl0g9>JJb4Bs--F(=9U1vdzNgLW@ZAXhE!Dyp~u&66DMB{=@=hfO>~^ zLL03UV}((L%p;t%r*F~&xhLxOUTD)_=vF$0-bYW9VqzN48|U;E`U342wW8WZc{#CE z{#ZI9{t!POyA-_~xguN+|Hxj>{FT0ya+6}RQqa^N;LUXRIT^c?z1oUdV@#7z=9Tzi zHYv9%?#lfv70IoVyIEKE2K$E91amC zhFQo7X4gKiyk z>!4c)-8$&jLAMUNbmI335)5 zbAp@`B*Y>i774LPh($sy5@L}Mi-cGt#3CUU39(3sMM5kRVv!JwgjgiRVzv%u>tMDH zX6stMDHX6s3XRa$?m~$A94aY zft)~2ASaL$$O+^GasoMloIp+>Cy*1!3FHKF0y&AZ@~nUjXPa1xJCy*1!Nym6gv5r(;_T}#r?CowX`V0use1ICy*1!3FHKF0y%-4Ku#bhmV3h6>@N1abm7ft)~2ASaL$$O+^GasoMloIp+>Cy*1!3FHKFa*MR4 zGwD8hnFchZ4n0S=({Z#ug`7Z6ASaL$$O+^GasoMloIp+>Cy*1!3FHKF0y%-4Ku+?b z`H}mC{4hUTC(|I^EY%@7AQ@gOIV6UYhV1abm7ft)~2ASaL$$O+^GasoMloIp+> zCyU9^SC+G675|!Cy*1!3FHKFa>!fb&kn{VpG`fP9-JAJ9T*N021N!&At#U%$O+^GasoMl zoIp+>Cy*1!3FHKF0y%-4Ku#bhkdsz)I{l1ZqCO3&OV86CbOLQie<6?)$O+^GasoMl zoIp+>Cy*1!3FHKF0y%-4Ku#bhkQ2yBlSmVxao9LpkZF-_m+Fx$3MTohy<;xq1abm7 zft)~2ASaL$$O+^GasoMloIp+>Cy*1!3FHKF(wG&ob(1Q zoIp+>Cy*1!3FHKF0y%-4Ku#bhkQ2xW#8_ce zA&?Wu3FHKF0y%-4Ku#bhkQ2xWCy*1!3FHKF0y%-4Ku&I2ovj^aUGrn!fnR4U z*dTU4E6t(|asoMloIp+>Cy*1!3FHKF0y%-4Ku#bhkQ2xWH5VvrNa3FHKF0y%-4Ku#bhkQ2xWCy*1!Nuf{} z7G~RII;6X&1|~-av;2+T_pazdP9P_c6UYhV1abm7ft)~2ASaL$$O+^GasoMloIp+> zCqvo0tb}#tpYq1$KC_qQTI=m0PE8kb0y%-4Ku#bhkQ2xWCy*1!3FHKF0y%-4Ku#bh zkQ2xWCy*1! z3FHKF0y%-4Ku#bhkdx8D&}8pamvqNW`)vELgU}(;KH56gA`Ur$oIp+>Cy*1!3FHKF z0y%-4Ku#bhkQ2xWCy*1!Ntduowp*rWdO+%# zCy*1!3FHKF0y%-4Ku#bhkQ2xWCy*1!$x*Gm)<>PE>`h#d(^6@vx>zUP zFxEH%+Gmu&e>Y_Bzu6B zWwG2?_nUQ2|WKu#bhkQ2xWuFFNw1Qrgphd#9#NY(pcH$raL^LQWtjkQ2xWQvBGHU2;>BE0y%-4Ku#bhkQ2xW zCy*1!3FHKF0y%-4JeX>dZjh;;tsmAG?vK=u)```MR~9u1asoMloIp+>Cy*1!3FHKF z0y%-4Ku#bhkQ2xWCy*1!3FHKF z0y%-4Ku#bhkQ2xWLNdcWiAt#U% z$O+^GasoMloIp+>Cy*1!3FHKF0y%-4Ku#bhkdtTB)ylUCOD-+nBNd2k;+Cy*1!3FPE!-k%3-BP(W2S$P&`QC6BY zU_%(>1abm7ft)~2ASaL$$O+^GasoMloIp+>Cy*1!3FHKFlAo@bsgbP_-Ywi6sS(YK zRf?AqBhpO?asoMloIp+>Cy*1!3FHKF0y%-4Ku#bhkQ2xWCy*1!3FHKF0y%-4Ku#bhkQ2xW, @@ -31,12 +41,24 @@ async function importSeedHelpers() { ) as Promise< typeof import('../../graphql/datasources/userKyselyPersistence.js') >, + import(`${TRANSPILED}/test/fixtureHelpers/createRule.js`) as Promise< + typeof import('../../test/fixtureHelpers/createRule.js') + >, + import(`${TRANSPILED}/test/fixtureHelpers/createMrtQueue.js`) as Promise< + typeof import('../../test/fixtureHelpers/createMrtQueue.js') + >, + import(`${TRANSPILED}/queues/itemSubmissionQueue.js`) as Promise< + typeof import('../../queues/itemSubmissionQueue.js') + >, ]); return { createOrg: createOrg.default, hashPassword: ums.hashPassword, UserRole: ums.UserRole, kyselyUserInsert: userPersistence.kyselyUserInsert, + createRule: createRule.default, + createMrtQueue: createMrtQueue.default, + ITEM_SUBMISSION_QUEUE_NAME: itemSubmissionQueue.ITEM_SUBMISSION_QUEUE_NAME, }; } @@ -49,6 +71,8 @@ export type SeededAdmin = { email: string; /** Plaintext password to log in with. */ password: string; + /** API key for the org's ingest endpoint (POST /api/v1/items/async). */ + apiKey: string; }; /** @@ -87,7 +111,144 @@ class Seeder { loginMethods: ['password'], }); - return { orgId: org.org.id, userId: user.id, email, password }; + return { + orgId: org.org.id, + userId: user.id, + email, + password, + apiKey: org.apiKey, + }; + } + + /** + * Create an MRT queue for the org. The first queue created for an org becomes + * the default queue (the destination for ENQUEUE_TO_MRT when no routing rule + * matches), so create exactly one queue before submitting if you rely on the + * default. The admin is assigned as a reviewer so the queue's jobs are + * visible to them via `reviewableQueues`. + */ + async createMrtQueue( + admin: SeededAdmin, + ): Promise<{ id: string; name: string }> { + const { createMrtQueue } = await importSeedHelpers(); + const { queue } = await createMrtQueue({ + orgId: admin.orgId, + mrtService: this.deps.ManualReviewToolService, + userId: admin.userId, + }); + return { id: queue.id, name: queue.name }; + } + + /** + * Create a LIVE content rule scoped to `itemTypeId` with the given + * `conditionSet` and `actionIds`. The conditionSet (what the rule matches) + * is owned by the caller; this factory just persists it via the + * `createRule` fixture helper. + */ + async createRule( + admin: SeededAdmin, + itemTypeId: string, + rule: { + conditionSet: unknown; + actionIds?: readonly string[]; + }, + ): Promise<{ id: string; name: string }> { + const { createRule } = await importSeedHelpers(); + const created = await createRule(this.deps.KyselyPg, admin.orgId, { + actionIds: rule.actionIds ?? [], + contentTypeIds: [itemTypeId], + conditionSet: rule.conditionSet as never, + }); + return { id: created.id, name: created.name }; + } + + /** + * Submit a content item via the real ingest endpoint (POST /api/v1/items/async), + * routed through the same origin the browser uses. The endpoint is async + * (202), so callers should waitForQueueDrained before reading the item. + */ + async submitContentItem( + request: APIRequestContext, + admin: SeededAdmin, + itemTypeId: string, + data: Record, + ): Promise<{ itemId: string }> { + const itemId = uid(); + const res = await request.post('/api/v1/items/async', { + headers: { 'x-api-key': admin.apiKey }, + data: { items: [{ id: itemId, typeId: itemTypeId, data }] }, + }); + if (res.status() !== 202) { + throw new Error( + `submitContentItem expected 202, got ${res.status()}: ${await res.text()}`, + ); + } + return { itemId }; + } + + /** + * Block until the item-submission BullMQ queue has no waiting or active jobs — + * i.e. every submitted item has been fully processed (written to Scylla, + * run through the rule engine, and any MRT jobs enqueued). Call this after + * `submitContentItem` and before navigating to a page that reads the + * processed item, so the read sees the data without the test having to + * poll the UI itself. + */ + async waitForQueueDrained(timeoutMs = 30_000): Promise { + const { ITEM_SUBMISSION_QUEUE_NAME } = await importSeedHelpers(); + const waitKey = `bull:${ITEM_SUBMISSION_QUEUE_NAME}:wait`; + const activeKey = `bull:${ITEM_SUBMISSION_QUEUE_NAME}:active`; + const redis = this.deps.IORedis; + const llen = async (key: string) => Number(await redis.llen(key)); + const deadline = Date.now() + timeoutMs; + while (true) { + if ((await llen(waitKey)) + (await llen(activeKey)) === 0) return; + if (Date.now() >= deadline) { + throw new Error('item-submission queue did not drain in time'); + } + await new Promise((resolve) => setTimeout(resolve, 100)); + } + } + + /** + * Authenticate as `admin` by calling the real `login` GraphQL mutation via + * `page.request`, which shares the page's cookie jar — so the session cookie + * the server sets lands on the browser automatically. Skips the UI login + * form, which other tests don't need to exercise (login.spec.ts does). After + * this, `page.goto('/dashboard/...')` works without re-authenticating. + */ + async login( + page: import('@playwright/test').Page, + admin: SeededAdmin, + ): Promise { + const res = await page.request.post('/api/v1/graphql', { + data: { + query: `mutation Login($input: LoginInput!) { + login(input: $input) { + __typename + ... on LoginSuccessResponse { user { id } } + ... on LoginUserDoesNotExistError { title } + ... on LoginIncorrectPasswordError { title } + } +}`, + variables: { input: { email: admin.email, password: admin.password } }, + }, + }); + if (!res.ok()) { + throw new Error( + `login mutation HTTP ${res.status()}: ${await res.text()}`, + ); + } + const body = (await res.json()) as { + data?: { login: { __typename: string } }; + errors?: unknown; + }; + const typename = body.data?.login.__typename; + if (typename !== 'LoginSuccessResponse') { + throw new Error( + `login failed: expected LoginSuccessResponse, got ${typename ?? 'no data'}`, + ); + } } } @@ -107,7 +268,26 @@ export const test = base.extend({ const { default: getBottle } = await importIocContainer(); const bottle = await getBottle(); const deps = bottle.container as Dependencies; + + // Start the item-processing worker inline so that content submitted via + // POST /api/v1/items/async gets drained from the Redis queue, run + // through the rule engine, and indexed — without a separate worker + // process. + const workerAbort = new AbortController(); + const workerRun = deps.ItemProcessingWorker.run(workerAbort.signal); + workerRun.catch((err) => { + console.error('ItemProcessingWorker exited with error', err); + }); + await use(deps); + + workerAbort.abort(); + try { + await deps.ItemProcessingWorker.shutdown(); + } catch { + // BullMQ's Worker.close() closes the shared ioredis connection, which + // can make closeSharedResourcesForShutdown throw on its own quit(). + } await deps.closeSharedResourcesForShutdown(); }, { scope: 'worker' }, diff --git a/server/e2e/fixtures/media.ts b/server/e2e/fixtures/media.ts new file mode 100644 index 00000000..f4fb0af8 --- /dev/null +++ b/server/e2e/fixtures/media.ts @@ -0,0 +1,11 @@ +/** + * Media URLs for e2e tests. The server's item-field validator only accepts + * http(s) URLs (no data URIs), and the audio must actually load to be played, + * so we serve a tiny fixture from the client dev server's `public/` dir + * (localhost is allowed via ALLOW_USER_INPUT_LOCALHOST_URIS=true). The image + * reuses the existing client logo. + */ + +export const IMAGE_URL = 'http://localhost:3000/logo192.png'; + +export const AUDIO_URL = 'http://localhost:3000/e2e/tone-3s.wav'; diff --git a/server/e2e/playwright.config.ts b/server/e2e/playwright.config.ts index be509c5e..8ba67d86 100644 --- a/server/e2e/playwright.config.ts +++ b/server/e2e/playwright.config.ts @@ -10,9 +10,12 @@ export default defineConfig({ // Configured paths resolve relative to this config file's dir (server/e2e/), // so these land at server/e2e/{test-results,playwright-report}. outputDir: 'test-results', - // Run every test concurrently. This helps ensure that we do - // not get implicit dependencies between tests. - fullyParallel: true, + // Run the suite serially. The tests share a single in-process BullMQ worker + // (the e2e `deps` fixture) and a content-type fixture path that races a + // global `REFRESH MATERIALIZED VIEW` trigger under concurrent inserts — so + // parallel `createContentType` calls intermittently return undefined. + fullyParallel: false, + workers: 1, // Fail the build on CI if test.only was left in the source. forbidOnly: Boolean(process.env.CI), // Retry flaky flows on CI; fail fast locally. diff --git a/server/e2e/tests/investigation.spec.ts b/server/e2e/tests/investigation.spec.ts new file mode 100644 index 00000000..465ff344 --- /dev/null +++ b/server/e2e/tests/investigation.spec.ts @@ -0,0 +1,38 @@ +import { ScalarTypes, type Field } from '@roostorg/coop-types'; +import { uid } from 'uid'; + +import { expect, test } from '../fixtures/coop.js'; + +test('a submitted item can be found in the investigation tool', async ({ + page, + request, + deps, + seed, +}) => { + const admin = await seed.orgWithAdmin(); + const itemType = await deps.ModerationConfigService.createContentType( + admin.orgId, + { + name: `type-${uid()}`, + schema: [ + { + name: 'text', + type: ScalarTypes.STRING, + required: true, + container: null, + }, + ] as [Field, ...Field[]], + schemaFieldRoles: {}, + }, + ); + const { itemId } = await seed.submitContentItem(request, admin, itemType.id, { + text: 'hello from e2e', + }); + await seed.waitForQueueDrained(); + + await seed.login(page, admin); + await page.goto('/dashboard/manual_review/investigation'); + await page.getByPlaceholder('Enter an item ID').fill(itemId); + await page.getByRole('button', { name: 'Search' }).click(); + await expect(page.getByText(itemType.name).first()).toBeVisible(); +}); diff --git a/server/e2e/tests/item-signals.spec.ts b/server/e2e/tests/item-signals.spec.ts new file mode 100644 index 00000000..624558fe --- /dev/null +++ b/server/e2e/tests/item-signals.spec.ts @@ -0,0 +1,60 @@ +import { ScalarTypes, type Field } from '@roostorg/coop-types'; +import { uid } from 'uid'; + +import { expect, jsonStringify, test } from '../fixtures/coop.js'; + +test('an item shows its rule execution / signal results in the investigation tool', async ({ + page, + request, + deps, + seed, +}) => { + const admin = await seed.orgWithAdmin(); + const itemType = await deps.ModerationConfigService.createContentType( + admin.orgId, + { + name: `type-${uid()}`, + schema: [ + { + name: 'text', + type: ScalarTypes.STRING, + required: true, + container: null, + }, + ] as [Field, ...Field[]], + schemaFieldRoles: {}, + }, + ); + const rule = await seed.createRule(admin, itemType.id, { + conditionSet: { + conjunction: 'AND', + conditions: [ + { + input: { + type: 'CONTENT_FIELD', + name: 'text', + contentTypeId: itemType.id, + }, + signal: { + id: jsonStringify({ type: 'TEXT_MATCHING_CONTAINS_TEXT' }), + type: 'TEXT_MATCHING_CONTAINS_TEXT', + }, + matchingValues: { strings: ['test'] }, + }, + ], + }, + }); + const { itemId } = await seed.submitContentItem(request, admin, itemType.id, { + text: 'this is a test', + }); + await seed.waitForQueueDrained(); + + await seed.login(page, admin); + await page.goto('/dashboard/manual_review/investigation'); + await page.getByPlaceholder('Enter an item ID').fill(itemId); + await page.getByRole('button', { name: 'Search' }).click(); + await expect(page.getByText(rule.name).first()).toBeVisible(); + await expect( + page.getByText('Matched', { exact: true }).first(), + ).toBeVisible(); +}); diff --git a/server/e2e/tests/item-type-creation.spec.ts b/server/e2e/tests/item-type-creation.spec.ts new file mode 100644 index 00000000..67a4dd74 --- /dev/null +++ b/server/e2e/tests/item-type-creation.spec.ts @@ -0,0 +1,48 @@ +import { uid } from 'uid'; + +import { expect, test } from '../fixtures/coop.js'; + +test('an admin creates an item type with mixed field types via the UI', async ({ + page, + seed, +}) => { + const admin = await seed.orgWithAdmin(); + await seed.login(page, admin); + + await page.goto('/dashboard/settings/item_types/form?kind=CONTENT'); + await expect(page.getByText('Create Item Type')).toBeVisible(); + + const typeName = `e2e-type-${uid()}`; + await page.locator('input[placeholder="Name"]').fill(typeName); + + const fields = [ + { name: 'text', type: 'String' }, + { name: 'image', type: 'Image' }, + { name: 'audio', type: 'Audio' }, + ]; + for (let i = 0; i < fields.length; i++) { + if (i > 0) { + await page.getByRole('button', { name: 'Add Field' }).click(); + } + await page + .locator('input[placeholder="Field Name"]') + .nth(i) + .fill(fields[i].name); + if (fields[i].type !== 'String') { + await page + .locator('.ant-select') + .nth(2 + i * 2) + .click(); + await page + .locator( + `.ant-select-dropdown:not(.ant-select-dropdown-hidden) .ant-select-item-option[title="${fields[i].type}"]`, + ) + .last() + .click(); + } + } + + await page.getByRole('button', { name: 'Create Content Type' }).click(); + await page.goto('/dashboard/settings/item_types'); + await expect(page.getByText(typeName)).toBeVisible(); +}); diff --git a/server/e2e/tests/login.spec.ts b/server/e2e/tests/login.spec.ts index acc5b9f2..5373ee75 100644 --- a/server/e2e/tests/login.spec.ts +++ b/server/e2e/tests/login.spec.ts @@ -1,6 +1,9 @@ import { expect, test } from '../fixtures/coop.js'; -test('a user can log in', async ({ page, seed }) => { +test('a user can log in and their session persists until logout', async ({ + page, + seed, +}) => { const admin = await seed.orgWithAdmin(); await page.goto('/login'); @@ -9,4 +12,17 @@ test('a user can log in', async ({ page, seed }) => { await page.getByRole('button', { name: 'Sign In' }).click(); await expect(page).toHaveURL(/\/dashboard/); + await page.goto('/dashboard/overview'); + await expect(page).toHaveURL(/\/dashboard\/overview/); + + await page.reload(); + await expect(page).toHaveURL(/\/dashboard\/overview/); + + const res = await page.request.post('/api/v1/graphql', { + data: { query: 'mutation { logout }' }, + }); + expect(res.ok()).toBeTruthy(); + + await page.goto('/dashboard/overview'); + await expect(page).toHaveURL(/\/login/); }); diff --git a/server/e2e/tests/mrt-job-review.spec.ts b/server/e2e/tests/mrt-job-review.spec.ts new file mode 100644 index 00000000..c8a3aa06 --- /dev/null +++ b/server/e2e/tests/mrt-job-review.spec.ts @@ -0,0 +1,111 @@ +import { ScalarTypes, type Field } from '@roostorg/coop-types'; +import { uid } from 'uid'; + +import { expect, jsonStringify, test } from '../fixtures/coop.js'; +import { AUDIO_URL, IMAGE_URL } from '../fixtures/media.js'; + +// VIDEO is intentionally omitted — react-player/lazy (used by +// ManualReviewJobContentBlurableVideo) crashes in vite dev mode ("Element type +// is invalid: lazy element must resolve to a class or function"), taking down +// the whole page (no per-field error boundary). Re-add VIDEO once that is fixed. +const FIELDS: Field[] = [ + { name: 'text', type: ScalarTypes.STRING, required: true, container: null }, + { name: 'image', type: ScalarTypes.IMAGE, required: false, container: null }, + { name: 'audio', type: ScalarTypes.AUDIO, required: false, container: null }, +]; + +test('an MRT job renders text/image/audio, plays audio, and records a decision', async ({ + page, + request, + deps, + seed, +}) => { + const admin = await seed.orgWithAdmin(); + const itemType = await deps.ModerationConfigService.createContentType( + admin.orgId, + { + name: `type-${uid()}`, + schema: FIELDS as [Field, ...Field[]], + schemaFieldRoles: {}, + }, + ); + const queue = await seed.createMrtQueue(admin); + const actions = await deps.ModerationConfigService.getActions({ + orgId: admin.orgId, + }); + const enqueueToMrt = actions.find((a) => a.actionType === 'ENQUEUE_TO_MRT'); + if (enqueueToMrt == null) { + throw new Error('ENQUEUE_TO_MRT built-in action not found for org'); + } + await seed.createRule(admin, itemType.id, { + actionIds: [enqueueToMrt.id], + conditionSet: { + conjunction: 'AND', + conditions: [ + { + input: { + type: 'CONTENT_FIELD', + name: 'text', + contentTypeId: itemType.id, + }, + signal: { + id: jsonStringify({ type: 'TEXT_MATCHING_CONTAINS_TEXT' }), + type: 'TEXT_MATCHING_CONTAINS_TEXT', + }, + matchingValues: { strings: ['test'] }, + }, + ], + }, + }); + + const uniqueText = `test media ${uid()}`; + await seed.submitContentItem(request, admin, itemType.id, { + text: uniqueText, + image: IMAGE_URL, + audio: AUDIO_URL, + }); + await seed.waitForQueueDrained(); + + await seed.login(page, admin); + await page.goto(`/dashboard/manual_review/queues/review/${queue.id}`); + await expect(page).toHaveURL(/\/review\/[^/]+\/[^/]+\/[^/]+/); + const jobId = new URL(page.url()).pathname.split('/').at(-2)!; + + await expect(page.getByText('Text', { exact: true })).toBeVisible(); + await expect(page.getByText('Image', { exact: true })).toBeVisible(); + await expect(page.getByText('Audio', { exact: true })).toBeVisible(); + + const audio = page.locator('audio').first(); + await expect(audio).toBeVisible(); + await audio.evaluate(async (el: HTMLAudioElement) => { + // eslint-disable-next-line functional/immutable-data -- DOM elements are mutable by nature. + el.muted = true; + await el.play(); + }); + + const submitResponse = page.waitForResponse( + (resp) => + resp.url().includes('/api/v1/graphql') && + resp.request().postData()?.includes('submitManualReviewDecision') === + true, + ); + await page + .getByTestId('manual-review-decision-action-list') + .getByText('Ignore', { exact: true }) + .click(); + await page.getByRole('button', { name: 'Submit' }).click(); + await submitResponse; + + const res = await page.request.post('/api/v1/graphql', { + data: { + query: `query GetDecidedJobFromJobId($id: String!) { + getDecidedJobFromJobId(id: $id) { decision { id } } +}`, + variables: { id: jobId }, + }, + }); + const body = (await res.json()) as { + data?: { getDecidedJobFromJobId?: { decision?: { id: string } } }; + }; + expect(body.data?.getDecidedJobFromJobId?.decision).not.toBeNull(); +}); diff --git a/server/e2e/tests/rule-creation.spec.ts b/server/e2e/tests/rule-creation.spec.ts new file mode 100644 index 00000000..da746f61 --- /dev/null +++ b/server/e2e/tests/rule-creation.spec.ts @@ -0,0 +1,72 @@ +import { ScalarTypes, type Field } from '@roostorg/coop-types'; +import { uid } from 'uid'; + +import { expect, test } from '../fixtures/coop.js'; + +test('an admin creates a content rule with a condition and an MRT action via the UI', async ({ + page, + deps, + seed, +}) => { + const admin = await seed.orgWithAdmin(); + const itemType = await deps.ModerationConfigService.createContentType( + admin.orgId, + { + name: `e2e-type-${uid()}`, + schema: [ + { + name: 'text', + type: ScalarTypes.STRING, + required: true, + container: null, + }, + ] as [Field, ...Field[]], + schemaFieldRoles: {}, + }, + ); + + await seed.login(page, admin); + await page.goto('/dashboard/rules/proactive/form'); + await expect(page.getByText('Create Rule').first()).toBeVisible(); + + const ruleName = `e2e-rule-${uid()}`; + await page.locator('input').first().fill(ruleName); + await page.locator('.ant-select').first().click(); + await page + .locator( + `.ant-select-dropdown:not(.ant-select-dropdown-hidden) .ant-select-item-option[title="${itemType.name}"]`, + ) + .click(); + await page.getByRole('button', { name: 'Continue' }).click(); + + await page.locator('.ant-select').nth(1).click(); + await page + .locator( + '.ant-select-dropdown:not(.ant-select-dropdown-hidden) .ant-select-item-option', + ) + .filter({ hasText: /^text$/ }) + .last() + .click(); + await page.getByRole('button', { name: 'Select Signal' }).click(); + await page.getByPlaceholder('Search').fill('Contains text'); + await page.getByText('Contains text', { exact: true }).click(); + await page.getByPlaceholder('Input Strings').fill('test'); + await page.getByPlaceholder('Input Strings').press('Enter'); + await page.getByRole('button', { name: 'Continue' }).click(); + + await page.locator('.ant-select').nth(2).click(); + await page + .locator( + '.ant-select-dropdown:not(.ant-select-dropdown-hidden) .ant-select-item-option', + ) + .filter({ hasText: 'Enqueue Item to Manual Review' }) + .last() + .click(); + await page.getByText('Live', { exact: true }).click(); + await page.getByRole('button', { name: 'Create Rule' }).click(); + + await expect(page.getByText('Rule Created')).toBeVisible(); + await page.getByRole('button', { name: 'OK' }).click(); + await expect(page).toHaveURL(/\/dashboard\/rules\/proactive/); + await expect(page.getByText(ruleName)).toBeVisible(); +}); diff --git a/server/e2e/tests/rule-routing.spec.ts b/server/e2e/tests/rule-routing.spec.ts new file mode 100644 index 00000000..e21e68e8 --- /dev/null +++ b/server/e2e/tests/rule-routing.spec.ts @@ -0,0 +1,67 @@ +import { ScalarTypes, type Field } from '@roostorg/coop-types'; +import { uid } from 'uid'; + +import { expect, jsonStringify, test } from '../fixtures/coop.js'; + +test('a rule routes a submitted item into a manual review queue', async ({ + page, + request, + deps, + seed, +}) => { + const admin = await seed.orgWithAdmin(); + const itemType = await deps.ModerationConfigService.createContentType( + admin.orgId, + { + name: `type-${uid()}`, + schema: [ + { + name: 'text', + type: ScalarTypes.STRING, + required: true, + container: null, + }, + ] as [Field, ...Field[]], + schemaFieldRoles: {}, + }, + ); + const queue = await seed.createMrtQueue(admin); + const actions = await deps.ModerationConfigService.getActions({ + orgId: admin.orgId, + }); + const enqueueToMrt = actions.find((a) => a.actionType === 'ENQUEUE_TO_MRT'); + if (enqueueToMrt == null) { + throw new Error('ENQUEUE_TO_MRT built-in action not found for org'); + } + await seed.createRule(admin, itemType.id, { + actionIds: [enqueueToMrt.id], + conditionSet: { + conjunction: 'AND', + conditions: [ + { + input: { + type: 'CONTENT_FIELD', + name: 'text', + contentTypeId: itemType.id, + }, + signal: { + id: jsonStringify({ type: 'TEXT_MATCHING_CONTAINS_TEXT' }), + type: 'TEXT_MATCHING_CONTAINS_TEXT', + }, + matchingValues: { strings: ['test'] }, + }, + ], + }, + }); + + const uniqueText = `test-${uid()}`; + await seed.submitContentItem(request, admin, itemType.id, { + text: uniqueText, + }); + await seed.waitForQueueDrained(); + + await seed.login(page, admin); + await page.goto(`/dashboard/manual_review/queues/jobs/${queue.id}`); + await expect(page.getByText(`Jobs in ${queue.name}`)).toBeVisible(); + await expect(page.getByText(uniqueText)).toBeVisible(); +}); diff --git a/server/utils/encoding.ts b/server/utils/encoding.ts index 2414c1e8..fef31842 100644 --- a/server/utils/encoding.ts +++ b/server/utils/encoding.ts @@ -1,7 +1,7 @@ import stringify from 'safe-stable-stringify'; import { type Opaque } from 'type-fest'; -import { JSON } from './json-schema-types.js'; +import { type JSON } from './json-schema-types.js'; /** * This function accepts any JS string and encodes it in base64, using a UTF8 From b1e6fb12c49b1cb5e45379595212d7ac56dcdf9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tao=20Bojl=C3=A9n?= Date: Mon, 13 Jul 2026 11:21:03 +0100 Subject: [PATCH 09/57] ci(client): run frontend tests in CI (#891) Co-authored-by: Pi --- .github/workflows/apply_pr_checks.yaml | 3 ++ client/src/components/Sidebar.test.tsx | 29 +++++++++---------- client/src/components/Sidebar.tsx | 2 ++ client/src/coop-ui/Calendar.test.tsx | 2 ++ .../MergedReportsComponent.test.tsx | 4 +-- 5 files changed, 23 insertions(+), 17 deletions(-) diff --git a/.github/workflows/apply_pr_checks.yaml b/.github/workflows/apply_pr_checks.yaml index 0ae20b88..74877371 100644 --- a/.github/workflows/apply_pr_checks.yaml +++ b/.github/workflows/apply_pr_checks.yaml @@ -277,6 +277,9 @@ jobs: - name: Lint client run: docker compose run --rm --quiet-pull client npm run lint + - name: Test client + run: docker compose run --rm --quiet-pull client npm run test:prepush + - name: Build client run: docker compose run --rm --quiet-pull client npm run build diff --git a/client/src/components/Sidebar.test.tsx b/client/src/components/Sidebar.test.tsx index 699d2467..c7b57843 100644 --- a/client/src/components/Sidebar.test.tsx +++ b/client/src/components/Sidebar.test.tsx @@ -82,10 +82,8 @@ function renderSidebar( return { ...result, setSelectedMenuItem, logout }; } -function getSettingsLink() { - return screen - .getAllByRole('link') - .find((el) => el.getAttribute('href') === '/dashboard/settings'); +function getSettingsButton() { + return screen.queryByRole('button', { name: 'Settings' }); } describe('Sidebar', () => { @@ -93,7 +91,7 @@ describe('Sidebar', () => { renderSidebar(); expect(screen.getByText('Overview')).toBeInTheDocument(); expect(screen.getByText('Review Console')).toBeInTheDocument(); - expect(getSettingsLink()).toBeDefined(); + expect(getSettingsButton()).toBeInTheDocument(); }); it('shows settings sub-items when on a settings page', () => { @@ -112,20 +110,21 @@ describe('Sidebar', () => { it('highlights gear icon on settings pages but not elsewhere', () => { const { unmount } = renderSidebar('/dashboard/settings', 'Settings'); - expect(getSettingsLink()!.className).toContain('text-primary'); + expect(getSettingsButton()).toHaveClass('text-primary'); unmount(); renderSidebar('/dashboard/overview', 'Overview'); - expect(getSettingsLink()!.className).not.toContain('text-primary'); + expect(getSettingsButton()).not.toHaveClass('text-primary'); }); - it('sets selectedMenuItem to Settings when gear icon is clicked', () => { - const { setSelectedMenuItem } = renderSidebar( - '/dashboard/overview', - 'Overview', - ); - fireEvent.click(getSettingsLink()!); - expect(setSelectedMenuItem).toHaveBeenCalledWith('Settings'); + it('expands settings sub-items when gear icon is clicked', () => { + renderSidebar('/dashboard/overview', 'Overview'); + const settingsButton = getSettingsButton(); + expect(settingsButton).toHaveAttribute('aria-expanded', 'false'); + + fireEvent.click(settingsButton!); + + expect(settingsButton).toHaveAttribute('aria-expanded', 'true'); }); describe('path-based menu selection', () => { @@ -142,7 +141,7 @@ describe('Sidebar', () => { it('hides gear icon when user lacks ManageOrg permission', () => { renderSidebar('/dashboard/overview', 'Overview', { permissions: [] }); - expect(getSettingsLink()).toBeUndefined(); + expect(getSettingsButton()).not.toBeInTheDocument(); }); it('hides sub-items when user lacks permissions', () => { diff --git a/client/src/components/Sidebar.tsx b/client/src/components/Sidebar.tsx index f910c17f..2c4c9518 100644 --- a/client/src/components/Sidebar.tsx +++ b/client/src/components/Sidebar.tsx @@ -352,6 +352,8 @@ export default function Sidebar(props: SidebarProps) {
setIsSettingsMenuExpanded((prev) => !prev)} onKeyDown={(e) => { diff --git a/client/src/coop-ui/Calendar.test.tsx b/client/src/coop-ui/Calendar.test.tsx index 32772c59..b4e4bc1b 100644 --- a/client/src/coop-ui/Calendar.test.tsx +++ b/client/src/coop-ui/Calendar.test.tsx @@ -15,6 +15,7 @@ describe('Calendar Component', () => { renderCalendar({ mode: 'single', selected: selectedDate, + defaultMonth: selectedDate, }); const selectedDay = screen.getByText('15'); @@ -26,6 +27,7 @@ describe('Calendar Component', () => { renderCalendar({ mode: 'multiple', selected: selectedDates, + defaultMonth: selectedDates[0], }); selectedDates.forEach((date) => { diff --git a/client/src/webpages/dashboard/mrt/manual_review_job/MergedReportsComponent.test.tsx b/client/src/webpages/dashboard/mrt/manual_review_job/MergedReportsComponent.test.tsx index b1068909..0583ab89 100644 --- a/client/src/webpages/dashboard/mrt/manual_review_job/MergedReportsComponent.test.tsx +++ b/client/src/webpages/dashboard/mrt/manual_review_job/MergedReportsComponent.test.tsx @@ -90,7 +90,7 @@ describe('MergedReportsComponent invalidation actions', () => { // Expand the table; collapsed by default. screen.getByRole('button', { name: /show/i }).click(); const buttons = screen.getAllByRole('button', { - name: /invalidate reports/i, + name: /invalidate all reports/i, }); expect(buttons).toHaveLength(2); }); @@ -99,7 +99,7 @@ describe('MergedReportsComponent invalidation actions', () => { renderMerged(false); screen.getByRole('button', { name: /show/i }).click(); expect( - screen.queryByRole('button', { name: /invalidate reports/i }), + screen.queryByRole('button', { name: /invalidate all reports/i }), ).not.toBeInTheDocument(); }); }); From b50b60d2f1df569d9f8a169c0769af3dacff0f55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tao=20Bojl=C3=A9n?= Date: Mon, 13 Jul 2026 15:09:16 +0100 Subject: [PATCH 10/57] ci: stop logging every DB query in test runs (#888) * ci: stop logging every DB query in test runs Set DATABASE_PRINT_LOGS=false in .env.githubci (used by the docker-compose `test` service for unit/integration tests) and comment it out in server/.env.example (copied to server/.env by the e2e workflow) so the default is off. Kysely still logs query errors regardless, so failures remain visible; this only suppresses the per-query SQL/params/duration spam that made CI logs unreadable. Co-Authored-By: pi * ci: drop redundant comment in server/.env.example Co-Authored-By: pi --------- Co-authored-by: pi Co-authored-by: Cassidy James --- .env.githubci | 2 +- server/.env.example | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.env.githubci b/.env.githubci index 16cc268e..57acd31d 100644 --- a/.env.githubci +++ b/.env.githubci @@ -50,7 +50,7 @@ SCYLLA_HAS_ENTERPRISE_FEATURES='false' GRAPHQL_MAX_DEPTH=10 EXPOSE_SENSITIVE_IMPLEMENTATION_DETAILS_IN_ERRORS=true ALLOW_USER_INPUT_LOCALHOST_URIS=true -DATABASE_PRINT_LOGS=true +DATABASE_PRINT_LOGS=false GROQ_SECRET_KEY= diff --git a/server/.env.example b/server/.env.example index 774142aa..f91cf6e2 100644 --- a/server/.env.example +++ b/server/.env.example @@ -33,7 +33,7 @@ DATABASE_IDLE_IN_TRANSACTION_TIMEOUT_MS=300000 # Delay (ms) before sending the first keepalive probe. Unset => OS default. # DATABASE_KEEPALIVE_INITIAL_DELAY_MS=0 # Log every Kysely query (SQL, params, duration). Errors always log regardless. -DATABASE_PRINT_LOGS=true +# DATABASE_PRINT_LOGS=true # Used to sign session ids in cookies. From 61a7e5eccb95f616741bdc5c5da1cb72c651c808 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tao=20Bojl=C3=A9n?= Date: Tue, 14 Jul 2026 10:44:28 +0100 Subject: [PATCH 11/57] test: migrate DB-backed tests to transactional harness (#821) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: close express-session store on api shutdown connect-pg-simple keeps a recurring pruneSessions timer that calls pool.query on its pool. makeApiServer created the store with the shared KyselyPgPool but never closed it, so the timer kept running after shutdown. In tests this leaked a setInterval per makeMockedServer() call that fired pool.query on the harness's already-closed pinned connection after each test, logging "Failed to prune sessions: Client was closed and is not queryable" indefinitely and hanging the test worker — the loop that forced the manual cancellation of CI run 27951870325. Hold a reference to the store instance and call its close() in the shutdown path. ownsPg is false (we pass in our own pool), so close() only stops the prune timer and won't end the shared pool. Co-Authored-By: Claude Opus 4.8 * test: migrate DB-backed tests to transactional harness Move the remaining DB-backed server tests onto the transaction-rollback harness from #732 so they get per-test isolation with no hand-written cleanup. - Convert the makeMockedServer/getBottle-based tests to makeTransactionalTestWithFixture, dropping manual org/user/queue/action deletes and KyselyPg.destroy() teardown: userKyselyPersistenceFindByEmailAndOrg, resolveSamlUser, and the MRT module tests (CommentOperations, JobRouting, QueueOperations, ReporterInvalidation). - Rewrite moderationConfigService and manualReviewToolService so every test is self-contained: each creates its own fresh org (rolled back automatically) rather than sharing a suite-scoped org. This removes the cross-test ordering/accumulator dependency in moderationConfigService and the hardcoded staging-seed-data dependency in manualReviewToolService. Scylla-touching tests (itemInvestigationService) keep uid-based isolation, since the Postgres-only harness can't roll those back. Co-Authored-By: Claude Opus 4.8 (1M context) * test: migrate UserReportSweep and userStrikeService to transactional harness Two DB-backed test suites were missed by the initial migration commit: - manualReviewToolService/modules/UserReportSweep.test.ts: a sibling of the four MRT module tests already migrated (CommentOperations, JobRouting, QueueOperations, ReporterInvalidation). It used makeTestWithFixture + getBottle with hand-written cleanup (createOrg/createUser/createMrtQueue teardown + KyselyPg/KyselyPgReadReplica.destroy()). Converted to makeTransactionalTestWithFixture; cleanup is now automatic rollback. - userStrikeService/userStrikeService.test.ts: used beforeAll/afterAll with a shared getBottle container and uid-based isolation. Converted to makeTransactionalTestWithFixture for true per-test rollback isolation. Also fixes a copy-paste describe block name ('Item Investigation Service' -> 'User Strike Service'). Co-Authored-By: Claude Opus 4.8 (1M context) * fix: resolve typecheck errors in migrated test files Two files migrated by the prior commit had TypeScript errors that broke `tsc` (and therefore `check_api_server` and the e2e server start): - manualReviewToolService.test.ts: the 'records an AUTOMATIC_CLOSE decision with no human reviewer' test was left as a bare `it(...)` referencing an out-of-scope `mrtService` and hardcoded staging `orgId`/'queueId' ('e7c89ce7729'/'1') — the very seed-data dependency the migration was meant to remove. Convert it to `testWithQueue()` like its siblings, using the fixture's `mrtService`/`org.id`/`queue.id`. - moderationConfigService.test.ts: the 'should return actions for a rule scoped to the caller org' snapshot read `it.id` from `getActionsForRuleId`, which returns `{ action, parameters }[]` — should be `it.action.id` (as on main). Also add the now-present `email` field role to the #createUserType inline snapshot. Co-Authored-By: Claude Opus 4.8 (1M context) * fix: create real user in migrated JobRouting fixture The transactional JobRouting fixture accidentally replaced the pre-migration createUser() call with a bare uid(). createManualReviewQueue validates queue users, so setup failed after creating org/item-type rows but before the fixture returned. Because makeTestWithFixture cannot run cleanup when setup throws, the outer transaction stayed idle-in-transaction and later tests blocked on the item_type_versions materialized-view refresh, timing out check_api_server. Keep the transactional harness, but create a real user and pass user.id to the queue setup. Co-Authored-By: Claude Opus 4.8 (1M context) * fix: restore QueueOperations org-scoping tests The rebase conflict resolution for QueueOperations.test.ts accidentally kept the pre-#872 branch side and dropped the two-org org-scoping regression tests that now exist on main. Restore testWithTwoOrgs and its cross-org access tests, using the transactional harness. Co-Authored-By: Claude Opus 4.8 (1M context) * test: configure decision-reason settings through service API Replace the decision-reason test helpers' direct writes to manual_review_tool_settings with a behavior-shaped helper that uses the ManualReviewToolService update API. The tests still exercise persisted org settings through submitDecision, but no longer couple setup to table and column names. Co-Authored-By: Claude Opus 4.8 (1M context) * style: remove api shutdown explanatory comments Remove the comments added around the express-session store shutdown while keeping the shutdown behavior unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) --------- Co-authored-by: Claude Opus 4.8 --- ...KyselyPersistenceFindByEmailAndOrg.test.ts | 46 +- server/graphql/utils/resolveSamlUser.test.ts | 68 +- .../manualReviewToolService.test.ts | 1389 +++++----- .../modules/CommentOperations.test.ts | 157 +- .../modules/JobRouting.test.ts | 336 ++- .../modules/QueueOperations.test.ts | 67 +- .../modules/ReporterInvalidation.test.ts | 44 +- .../modules/UserReportSweep.test.ts | 44 +- .../moderationConfigService.test.ts | 2236 ++++++++--------- .../userStrikeService.test.ts | 450 ++-- 10 files changed, 2355 insertions(+), 2482 deletions(-) diff --git a/server/graphql/datasources/userKyselyPersistenceFindByEmailAndOrg.test.ts b/server/graphql/datasources/userKyselyPersistenceFindByEmailAndOrg.test.ts index 7fdcc690..be771952 100644 --- a/server/graphql/datasources/userKyselyPersistenceFindByEmailAndOrg.test.ts +++ b/server/graphql/datasources/userKyselyPersistenceFindByEmailAndOrg.test.ts @@ -3,10 +3,8 @@ import { uid } from 'uid'; import { UserRole } from '../../services/userManagementService/index.js'; import createOrg from '../../test/fixtureHelpers/createOrg.js'; -import { makeMockedServer } from '../../test/setupMockedServer.js'; -import { makeTestWithFixture } from '../../test/utils.js'; +import { makeTransactionalTestWithFixture } from '../../test/harness/transactionalTest.js'; import { - kyselyUserDeleteById, kyselyUserFindByEmailAndOrg, kyselyUserInsert, } from './userKyselyPersistence.js'; @@ -25,9 +23,8 @@ function samlUserInput(orgId: string) { } describe('kyselyUserFindByEmailAndOrg', () => { - const testWithFixture = makeTestWithFixture(async () => { - const { deps, shutdown } = await makeMockedServer(); - const { org, cleanup: orgCleanup } = await createOrg( + const testWithFixture = makeTransactionalTestWithFixture(async ({ deps }) => { + const { org } = await createOrg( { KyselyPg: deps.KyselyPg, ModerationConfigService: deps.ModerationConfigService, @@ -35,14 +32,7 @@ describe('kyselyUserFindByEmailAndOrg', () => { }, uid(), ); - return { - deps, - org, - async cleanup() { - await orgCleanup(); - await shutdown(); - }, - }; + return { org }; }); testWithFixture( @@ -50,15 +40,11 @@ describe('kyselyUserFindByEmailAndOrg', () => { async ({ deps, org }) => { const input = samlUserInput(org.id); await kyselyUserInsert({ db: deps.KyselyPg, ...input }); - try { - const result = await kyselyUserFindByEmailAndOrg(deps.KyselyPg, { - email: input.email, - orgId: org.id, - }); - expect(result).toMatchObject({ id: input.id, orgId: org.id }); - } finally { - await kyselyUserDeleteById(deps.KyselyPg, input.id); - } + const result = await kyselyUserFindByEmailAndOrg(deps.KyselyPg, { + email: input.email, + orgId: org.id, + }); + expect(result).toMatchObject({ id: input.id, orgId: org.id }); }, ); @@ -69,15 +55,11 @@ describe('kyselyUserFindByEmailAndOrg', () => { async ({ deps, org }) => { const input = samlUserInput(org.id); await kyselyUserInsert({ db: deps.KyselyPg, ...input }); - try { - const result = await kyselyUserFindByEmailAndOrg(deps.KyselyPg, { - email: input.email, - orgId: `different-org-${uid()}`, - }); - expect(result).toBeUndefined(); - } finally { - await kyselyUserDeleteById(deps.KyselyPg, input.id); - } + const result = await kyselyUserFindByEmailAndOrg(deps.KyselyPg, { + email: input.email, + orgId: `different-org-${uid()}`, + }); + expect(result).toBeUndefined(); }, ); diff --git a/server/graphql/utils/resolveSamlUser.test.ts b/server/graphql/utils/resolveSamlUser.test.ts index c28154c5..3c7d0174 100644 --- a/server/graphql/utils/resolveSamlUser.test.ts +++ b/server/graphql/utils/resolveSamlUser.test.ts @@ -5,11 +5,9 @@ import { uid } from 'uid'; import { UserRole } from '../../services/userManagementService/index.js'; import createOrg from '../../test/fixtureHelpers/createOrg.js'; -import { makeMockedServer } from '../../test/setupMockedServer.js'; -import { makeTestWithFixture } from '../../test/utils.js'; +import { makeTransactionalTestWithFixture } from '../../test/harness/transactionalTest.js'; import { type default as SafeTracer } from '../../utils/SafeTracer.js'; import { - kyselyUserDeleteById, kyselyUserInsert, type UsersDb, } from '../datasources/userKyselyPersistence.js'; @@ -33,9 +31,8 @@ function samlUserInput(orgId: string) { } describe('resolveSamlUser', () => { - const testWithFixture = makeTestWithFixture(async () => { - const { deps, shutdown } = await makeMockedServer(); - const { org, cleanup: orgCleanup } = await createOrg( + const testWithFixture = makeTransactionalTestWithFixture(async ({ deps }) => { + const { org } = await createOrg( { KyselyPg: deps.KyselyPg, ModerationConfigService: deps.ModerationConfigService, @@ -43,14 +40,7 @@ describe('resolveSamlUser', () => { }, uid(), ); - return { - deps, - org, - async cleanup() { - await orgCleanup(); - await shutdown(); - }, - }; + return { org }; }); testWithFixture( @@ -59,21 +49,17 @@ describe('resolveSamlUser', () => { const input = samlUserInput(org.id); await kyselyUserInsert({ db: deps.KyselyPg, ...input }); const done = jest.fn(); - try { - await resolveSamlUser( - deps.KyselyPg, - deps.Tracer, - makeReq(org.id), - { email: input.email }, - done, - ); - expect(done).toHaveBeenCalledTimes(1); - const [err, user] = done.mock.calls[0]; - expect(err).toBeNull(); - expect(user).toMatchObject({ id: input.id, orgId: org.id }); - } finally { - await kyselyUserDeleteById(deps.KyselyPg, input.id); - } + await resolveSamlUser( + deps.KyselyPg, + deps.Tracer, + makeReq(org.id), + { email: input.email }, + done, + ); + expect(done).toHaveBeenCalledTimes(1); + const [err, user] = done.mock.calls[0]; + expect(err).toBeNull(); + expect(user).toMatchObject({ id: input.id, orgId: org.id }); }, ); @@ -85,20 +71,16 @@ describe('resolveSamlUser', () => { const input = samlUserInput(org.id); await kyselyUserInsert({ db: deps.KyselyPg, ...input }); const done = jest.fn(); - try { - await resolveSamlUser( - deps.KyselyPg, - deps.Tracer, - makeReq(`different-org-${uid()}`), - { email: input.email }, - done, - ); - const [err, user] = done.mock.calls[0]; - expect(err).toBeInstanceOf(Error); - expect(user).toBeUndefined(); - } finally { - await kyselyUserDeleteById(deps.KyselyPg, input.id); - } + await resolveSamlUser( + deps.KyselyPg, + deps.Tracer, + makeReq(`different-org-${uid()}`), + { email: input.email }, + done, + ); + const [err, user] = done.mock.calls[0]; + expect(err).toBeInstanceOf(Error); + expect(user).toBeUndefined(); }, ); diff --git a/server/services/manualReviewToolService/manualReviewToolService.test.ts b/server/services/manualReviewToolService/manualReviewToolService.test.ts index 73293091..8c100b1a 100644 --- a/server/services/manualReviewToolService/manualReviewToolService.test.ts +++ b/server/services/manualReviewToolService/manualReviewToolService.test.ts @@ -1,7 +1,12 @@ /* eslint-disable max-lines */ +import { uid } from 'uid'; import { v1 as uuidv1 } from 'uuid'; -import getBottle, { type Dependencies } from '../../iocContainer/index.js'; +import createMrtQueue from '../../test/fixtureHelpers/createMrtQueue.js'; +import createOrg from '../../test/fixtureHelpers/createOrg.js'; +import createUser from '../../test/fixtureHelpers/createUser.js'; +import { makeTransactionalTestWithFixture } from '../../test/harness/transactionalTest.js'; +import { type MockedServer } from '../../test/setupMockedServer.js'; import { instantiateOpaqueType } from '../../utils/typescript-types.js'; import { makeSubmissionId, @@ -16,6 +21,8 @@ import { import { AUTOMATED_DECISION_REVIEWER_ID } from './modules/JobDecisioning.js'; import { jobIdToGuid } from './modules/QueueOperations.js'; +type TestDeps = MockedServer['deps']; + function makeDummyJob() { return { createdAt: new Date(), @@ -73,145 +80,128 @@ function makeDummyNcmecJob() { }; } -describe('Manual Review Tool Service', () => { - let mrtService: ManualReviewToolService; - let container: Dependencies; - - beforeAll(async () => { - // The mutation should be ok here since this is initial setup in a - // beforeAll; it doesn't involve reset state for each test in the suite - - ({ container } = await getBottle()); - mrtService = container.ManualReviewToolService; - }); - - afterAll(async () => { - await container.closeSharedResourcesForShutdown(); - }); +async function configureDecisionReasonRequirements( + mrtService: ManualReviewToolService, + orgId: string, + opts: { + onAction?: boolean; + onIgnore?: boolean; + }, +) { + if (opts.onAction !== undefined) { + await mrtService.updateRequiresDecisionReason(orgId, opts.onAction); + } + if (opts.onIgnore !== undefined) { + await mrtService.updateRequiresDecisionReasonOnIgnore(orgId, opts.onIgnore); + } +} - // Test that we can start the stalled jobs checker for manual job processing - test('should be able to start stalled jobs checker', async () => { - const worker = await mrtService['queueOps']['getBullWorker']({ - orgId: 'dummyOrg', - queueId: 'dummyQueue', - }); - // The startStalledCheckTimer method should be available and not throw - expect(worker).toBeDefined(); - }); +async function setRequiresPolicyForDecisions( + mrtService: ManualReviewToolService, + db: TestDeps['KyselyPg'], + orgId: string, + value: boolean, +) { + await mrtService.upsertDefaultSettings({ orgId }); + await db + .updateTable('manual_review_tool.manual_review_tool_settings') + .set({ requires_policy_for_decisions: value }) + .where('org_id', '=', orgId) + .execute(); +} - // TODO: rework when we rework the MRT error handling - test.skip('MRT throws for submitting a job that has already been moved to completed', async () => { - const orgId = 'e7c89ce7729', - queueId = '1', - reviewerId = uuidv1(), - reviewerEmail = 'test@test.com', - itemId = uuidv1(), - itemTypeId = uuidv1(); - - await mrtService['queueOps']['addJob']({ - queueId, - enqueueSourceInfo: { kind: 'REPORT' }, - jobPayload: { - createdAt: new Date(), - payload: { - kind: 'DEFAULT', - reportHistory: [], - item: instantiateOpaqueType({ - submissionId: makeSubmissionId(), - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - data: {} as NormalizedItemData, - itemTypeIdentifier: { - id: itemTypeId, - version: new Date().toISOString(), - schemaVariant: 'original', - }, - creator: { - id: uuidv1(), - typeId: uuidv1(), - }, - itemId, - }), - reportedForReason: undefined, - reportedForReasons: [], - enqueueSourceInfo: { kind: 'REPORT' }, - }, - policyIds: [], +describe('Manual Review Tool Service', () => { + // Just the service — for cases that don't need any org-scoped fixtures. + const testWithService = makeTransactionalTestWithFixture( + async ({ deps }) => ({ + mrtService: deps.ManualReviewToolService, + }), + ); + + // A fresh org with a queue and a CUSTOM_ACTION, so decision tests can enqueue + // a job and submit a real (validatable) action without relying on seed data. + const testWithQueue = makeTransactionalTestWithFixture(async ({ deps }) => { + const mrtService = deps.ManualReviewToolService; + const { org } = await createOrg( + { + KyselyPg: deps.KyselyPg, + ModerationConfigService: deps.ModerationConfigService, + ApiKeyService: deps.ApiKeyService, }, - orgId, - }); - - const dequeuedJob = await mrtService.dequeueNextJob({ - orgId, - queueId, - userId: reviewerId, + uid(), + ); + const { user } = await createUser(deps.KyselyPg, org.id); + const { queue } = await createMrtQueue({ + orgId: org.id, + mrtService, + userId: user.id, }); - - if (!dequeuedJob) { - throw new Error('should have dequeued successfully.'); - } - - await mrtService.submitDecision({ - queueId, - reportHistory: [], - jobId: dequeuedJob.job.id, - lockToken: dequeuedJob.lockToken, - decisionComponents: [ - { - type: 'CUSTOM_ACTION', - actions: [{ id: '8481310e8c4' }], - policies: [], - itemIds: [itemId], - itemTypeId, - }, - ], - relatedActions: [], - reviewerId, - reviewerEmail, - orgId, + const action = await deps.ModerationConfigService.createAction(org.id, { + name: `mrt-test-action-${uid()}`, + description: null, + type: 'CUSTOM_ACTION', + callbackUrl: 'https://example.com', + callbackUrlHeaders: null, + callbackUrlBody: null, }); - const duplicativeDecision = async () => { - return mrtService.submitDecision({ - queueId, - reportHistory: [], - jobId: dequeuedJob.job.id, - lockToken: dequeuedJob.lockToken, - decisionComponents: [ - { - type: 'CUSTOM_ACTION', - actions: [{ id: '8481310e8c4' }], - policies: [], - itemIds: [itemId], - itemTypeId, - }, - ], - relatedActions: [], - reviewerId, - reviewerEmail, - orgId, - }); - }; - - await expect(duplicativeDecision()).rejects.toThrow( - `No job with ID ${dequeuedJob.job.id} in queue with ID ${queueId}`, - ); + return { mrtService, org, user, queue, actionId: action.id }; }); - describe('duplicate decision handling', () => { - it('should reject duplicate decisions with the same lock token', async () => { - const orgId = 'e7c89ce7729', + // Test that we can start the stalled jobs checker for manual job processing + testWithService( + 'should be able to start stalled jobs checker', + async ({ mrtService }) => { + const worker = await mrtService['queueOps']['getBullWorker']({ + orgId: 'dummyOrg', + queueId: 'dummyQueue', + }); + // The startStalledCheckTimer method should be available and not throw + expect(worker).toBeDefined(); + }, + ); + + // TODO: rework when we rework the MRT error handling + testWithService.skip( + 'MRT throws for submitting a job that has already been moved to completed', + async ({ mrtService }) => { + const orgId = uid(), queueId = '1', reviewerId = uuidv1(), reviewerEmail = 'test@test.com', - jobPayload = makeDummyJob(); - const itemId = jobPayload.payload.item.itemId, - itemTypeId = jobPayload.payload.item.itemTypeIdentifier.id; + itemId = uuidv1(), + itemTypeId = uuidv1(); await mrtService['queueOps']['addJob']({ - jobPayload, - orgId, queueId, enqueueSourceInfo: { kind: 'REPORT' }, + jobPayload: { + createdAt: new Date(), + payload: { + kind: 'DEFAULT', + reportHistory: [], + item: instantiateOpaqueType({ + submissionId: makeSubmissionId(), + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions + data: {} as NormalizedItemData, + itemTypeIdentifier: { + id: itemTypeId, + version: new Date().toISOString(), + schemaVariant: 'original', + }, + creator: { + id: uuidv1(), + typeId: uuidv1(), + }, + itemId, + }), + reportedForReason: undefined, + reportedForReasons: [], + enqueueSourceInfo: { kind: 'REPORT' }, + }, + policyIds: [], + }, + orgId, }); const dequeuedJob = await mrtService.dequeueNextJob({ @@ -221,7 +211,7 @@ describe('Manual Review Tool Service', () => { }); if (!dequeuedJob) { - throw new Error("should've returned a job"); + throw new Error('should have dequeued successfully.'); } await mrtService.submitDecision({ @@ -245,7 +235,7 @@ describe('Manual Review Tool Service', () => { }); const duplicativeDecision = async () => { - await mrtService.submitDecision({ + return mrtService.submitDecision({ queueId, reportHistory: [], jobId: dequeuedJob.job.id, @@ -266,8 +256,86 @@ describe('Manual Review Tool Service', () => { }); }; - await expect(duplicativeDecision()).rejects.toThrow(); - }); + await expect(duplicativeDecision()).rejects.toThrow( + `No job with ID ${dequeuedJob.job.id} in queue with ID ${queueId}`, + ); + }, + ); + + describe('duplicate decision handling', () => { + testWithQueue( + 'should reject duplicate decisions with the same lock token', + async ({ mrtService, org, queue, actionId }) => { + const orgId = org.id, + queueId = queue.id, + reviewerId = uuidv1(), + reviewerEmail = 'test@test.com', + jobPayload = makeDummyJob(); + const itemId = jobPayload.payload.item.itemId, + itemTypeId = jobPayload.payload.item.itemTypeIdentifier.id; + + await mrtService['queueOps']['addJob']({ + jobPayload, + orgId, + queueId, + enqueueSourceInfo: { kind: 'REPORT' }, + }); + + const dequeuedJob = await mrtService.dequeueNextJob({ + orgId, + queueId, + userId: reviewerId, + }); + + if (!dequeuedJob) { + throw new Error("should've returned a job"); + } + + await mrtService.submitDecision({ + queueId, + reportHistory: [], + jobId: dequeuedJob.job.id, + lockToken: dequeuedJob.lockToken, + decisionComponents: [ + { + type: 'CUSTOM_ACTION', + actions: [{ id: actionId }], + policies: [], + itemIds: [itemId], + itemTypeId, + }, + ], + relatedActions: [], + reviewerId, + reviewerEmail, + orgId, + }); + + const duplicativeDecision = async () => { + await mrtService.submitDecision({ + queueId, + reportHistory: [], + jobId: dequeuedJob.job.id, + lockToken: dequeuedJob.lockToken, + decisionComponents: [ + { + type: 'CUSTOM_ACTION', + actions: [{ id: actionId }], + policies: [], + itemIds: [itemId], + itemTypeId, + }, + ], + relatedActions: [], + reviewerId, + reviewerEmail, + orgId, + }); + }; + + await expect(duplicativeDecision()).rejects.toThrow(); + }, + ); it.skip('should reject duplicate decisions on jobs dequeued again after the lock expires', async () => {}); }); @@ -278,51 +346,54 @@ describe('Manual Review Tool Service', () => { // stuck in a retry loop. The decision must record the empty-string // reviewer id (rendered as "Automatic" client-side) and not throw. describe('automatic close decisions', () => { - it('records an AUTOMATIC_CLOSE decision with no human reviewer', async () => { - const orgId = 'e7c89ce7729'; - const queueId = '1'; - const jobPayload = makeDummyJob(); - - await mrtService['queueOps']['addJob']({ - jobPayload, - orgId, - queueId, - enqueueSourceInfo: { kind: 'REPORT' }, - }); + testWithQueue( + 'records an AUTOMATIC_CLOSE decision with no human reviewer', + async ({ mrtService, org, queue }) => { + const orgId = org.id, + queueId = queue.id, + jobPayload = makeDummyJob(); + + await mrtService['queueOps']['addJob']({ + jobPayload, + orgId, + queueId, + enqueueSourceInfo: { kind: 'REPORT' }, + }); - const dequeuedJob = await mrtService.dequeueNextJob({ - orgId, - queueId, - userId: uuidv1(), - }); + const dequeuedJob = await mrtService.dequeueNextJob({ + orgId, + queueId, + userId: uuidv1(), + }); - if (!dequeuedJob) { - throw new Error("should've returned a job"); - } + if (!dequeuedJob) { + throw new Error("should've returned a job"); + } - // Used to throw 23502 (null reviewer_id) before the sentinel fix. - await mrtService.submitDecision({ - queueId, - reportHistory: [], - jobId: dequeuedJob.job.id, - lockToken: dequeuedJob.lockToken, - relatedActions: [], - orgId, - automaticCloseDecision: { - type: 'AUTOMATIC_CLOSE', - reason: 'ITEM_DELETED_BEFORE_REVIEW', - }, - }); + // Used to throw 23502 (null reviewer_id) before the sentinel fix. + await mrtService.submitDecision({ + queueId, + reportHistory: [], + jobId: dequeuedJob.job.id, + lockToken: dequeuedJob.lockToken, + relatedActions: [], + orgId, + automaticCloseDecision: { + type: 'AUTOMATIC_CLOSE', + reason: 'ITEM_DELETED_BEFORE_REVIEW', + }, + }); - const row = await mrtService['pgQuery'] - .selectFrom('manual_review_tool.manual_review_decisions') - .where('id', '=', jobIdToGuid(dequeuedJob.job.id)) - .where('org_id', '=', orgId) - .select(['reviewer_id']) - .executeTakeFirst(); + const row = await mrtService['pgQuery'] + .selectFrom('manual_review_tool.manual_review_decisions') + .where('id', '=', jobIdToGuid(dequeuedJob.job.id)) + .where('org_id', '=', orgId) + .select(['reviewer_id']) + .executeTakeFirst(); - expect(row?.reviewer_id).toBe(AUTOMATED_DECISION_REVIEWER_ID); - }); + expect(row?.reviewer_id).toBe(AUTOMATED_DECISION_REVIEWER_ID); + }, + ); }); // Issue #616: when an org sets `mrt_requires_decision_reason_on_action`, @@ -335,91 +406,100 @@ describe('Manual Review Tool Service', () => { // (`..._on_ignore`) — so the cases below also cover that an IGNORE decision is // gated by the ignore flag, not the action flag. describe('requires_decision_reason enforcement', () => { - const orgId = 'e7c89ce7729'; - const queueId = '1'; - // Pulled from the staging seed data — any CUSTOM_ACTION row on this org - // will do; the action-id validation runs before our reason check. - const seededActionId = '1873b2f15cc'; - - const setRequiresDecisionReason = async (value: boolean) => { - await mrtService.upsertDefaultSettings({ orgId }); - await mrtService['pgQuery'] - .updateTable('manual_review_tool.manual_review_tool_settings') - .set({ mrt_requires_decision_reason_on_action: value }) - .where('org_id', '=', orgId) - .execute(); - }; - - const setRequiresDecisionReasonOnIgnore = async (value: boolean) => { - await mrtService.upsertDefaultSettings({ orgId }); - await mrtService['pgQuery'] - .updateTable('manual_review_tool.manual_review_tool_settings') - .set({ mrt_requires_decision_reason_on_ignore: value }) - .where('org_id', '=', orgId) - .execute(); - }; - - beforeAll(async () => { - // The queue row must exist before addJob, but no other test in this - // file owns its lifecycle, so we seed it here idempotently. - await mrtService['pgQuery'] - .insertInto('manual_review_tool.manual_review_queues') - .values({ - id: queueId, - name: 'integ-test-queue', - description: null, - org_id: orgId, - is_default_queue: false, - is_appeals_queue: false, - auto_close_jobs: false, - }) - .onConflict((oc) => oc.doNothing()) - .execute(); - }); + testWithQueue( + 'rejects a decision with no reason when the flag is on', + async ({ mrtService, org, queue, actionId }) => { + await configureDecisionReasonRequirements(mrtService, org.id, { + onAction: true, + }); - afterEach(async () => { - // Reset so the flags don't leak into other tests in this file or - // subsequent runs that reuse the seeded org. - await setRequiresDecisionReason(false); - await setRequiresDecisionReasonOnIgnore(false); - }); + const reviewerId = uuidv1(); + const reviewerEmail = 'test@test.com'; + const jobPayload = makeDummyJob(); + const itemId = jobPayload.payload.item.itemId; + const itemTypeId = jobPayload.payload.item.itemTypeIdentifier.id; - it('rejects a decision with no reason when the flag is on', async () => { - await setRequiresDecisionReason(true); + await mrtService['queueOps']['addJob']({ + jobPayload, + orgId: org.id, + queueId: queue.id, + enqueueSourceInfo: { kind: 'REPORT' }, + }); - const reviewerId = uuidv1(); - const reviewerEmail = 'test@test.com'; - const jobPayload = makeDummyJob(); - const itemId = jobPayload.payload.item.itemId; - const itemTypeId = jobPayload.payload.item.itemTypeIdentifier.id; + const dequeuedJob = await mrtService.dequeueNextJob({ + orgId: org.id, + queueId: queue.id, + userId: reviewerId, + }); - await mrtService['queueOps']['addJob']({ - jobPayload, - orgId, - queueId, - enqueueSourceInfo: { kind: 'REPORT' }, - }); + if (!dequeuedJob) { + throw new Error("should've returned a job"); + } + + await expect( + mrtService.submitDecision({ + queueId: queue.id, + reportHistory: [], + jobId: dequeuedJob.job.id, + lockToken: dequeuedJob.lockToken, + decisionComponents: [ + { + type: 'CUSTOM_ACTION', + actions: [{ id: actionId }], + policies: [{ id: uuidv1() }], + itemIds: [itemId], + itemTypeId, + }, + ], + relatedActions: [], + reviewerId, + reviewerEmail, + orgId: org.id, + // decisionReason intentionally omitted + }), + ).rejects.toThrow(/requires every decision to include a reason/i); + }, + ); - const dequeuedJob = await mrtService.dequeueNextJob({ - orgId, - queueId, - userId: reviewerId, - }); + testWithQueue( + 'allows a decision with a reason when the flag is on', + async ({ mrtService, org, queue, actionId }) => { + await configureDecisionReasonRequirements(mrtService, org.id, { + onAction: true, + }); - if (!dequeuedJob) { - throw new Error("should've returned a job"); - } + const reviewerId = uuidv1(); + const reviewerEmail = 'test@test.com'; + const jobPayload = makeDummyJob(); + const itemId = jobPayload.payload.item.itemId; + const itemTypeId = jobPayload.payload.item.itemTypeIdentifier.id; - await expect( - mrtService.submitDecision({ - queueId, + await mrtService['queueOps']['addJob']({ + jobPayload, + orgId: org.id, + queueId: queue.id, + enqueueSourceInfo: { kind: 'REPORT' }, + }); + + const dequeuedJob = await mrtService.dequeueNextJob({ + orgId: org.id, + queueId: queue.id, + userId: reviewerId, + }); + + if (!dequeuedJob) { + throw new Error("should've returned a job"); + } + + await mrtService.submitDecision({ + queueId: queue.id, reportHistory: [], jobId: dequeuedJob.job.id, lockToken: dequeuedJob.lockToken, decisionComponents: [ { type: 'CUSTOM_ACTION', - actions: [{ id: seededActionId }], + actions: [{ id: actionId }], policies: [{ id: uuidv1() }], itemIds: [itemId], itemTypeId, @@ -428,140 +508,148 @@ describe('Manual Review Tool Service', () => { relatedActions: [], reviewerId, reviewerEmail, - orgId, - // decisionReason intentionally omitted - }), - ).rejects.toThrow(/requires every decision to include a reason/i); - }); + orgId: org.id, + decisionReason: 'Repeat offender', + }); + }, + ); - it('allows a decision with a reason when the flag is on', async () => { - await setRequiresDecisionReason(true); + testWithQueue( + 'allows a decision with no reason when the flag is off', + async ({ mrtService, org, queue, actionId }) => { + // Control case: default-off behavior must remain unchanged so orgs that + // never opt in see no difference from this PR. + await configureDecisionReasonRequirements(mrtService, org.id, { + onAction: false, + }); - const reviewerId = uuidv1(); - const reviewerEmail = 'test@test.com'; - const jobPayload = makeDummyJob(); - const itemId = jobPayload.payload.item.itemId; - const itemTypeId = jobPayload.payload.item.itemTypeIdentifier.id; + const reviewerId = uuidv1(); + const reviewerEmail = 'test@test.com'; + const jobPayload = makeDummyJob(); + const itemId = jobPayload.payload.item.itemId; + const itemTypeId = jobPayload.payload.item.itemTypeIdentifier.id; - await mrtService['queueOps']['addJob']({ - jobPayload, - orgId, - queueId, - enqueueSourceInfo: { kind: 'REPORT' }, - }); + await mrtService['queueOps']['addJob']({ + jobPayload, + orgId: org.id, + queueId: queue.id, + enqueueSourceInfo: { kind: 'REPORT' }, + }); - const dequeuedJob = await mrtService.dequeueNextJob({ - orgId, - queueId, - userId: reviewerId, - }); + const dequeuedJob = await mrtService.dequeueNextJob({ + orgId: org.id, + queueId: queue.id, + userId: reviewerId, + }); - if (!dequeuedJob) { - throw new Error("should've returned a job"); - } + if (!dequeuedJob) { + throw new Error("should've returned a job"); + } - await mrtService.submitDecision({ - queueId, - reportHistory: [], - jobId: dequeuedJob.job.id, - lockToken: dequeuedJob.lockToken, - decisionComponents: [ - { - type: 'CUSTOM_ACTION', - actions: [{ id: seededActionId }], - policies: [{ id: uuidv1() }], - itemIds: [itemId], - itemTypeId, - }, - ], - relatedActions: [], - reviewerId, - reviewerEmail, - orgId, - decisionReason: 'Repeat offender', - }); - }); + await mrtService.submitDecision({ + queueId: queue.id, + reportHistory: [], + jobId: dequeuedJob.job.id, + lockToken: dequeuedJob.lockToken, + decisionComponents: [ + { + type: 'CUSTOM_ACTION', + actions: [{ id: actionId }], + policies: [{ id: uuidv1() }], + itemIds: [itemId], + itemTypeId, + }, + ], + relatedActions: [], + reviewerId, + reviewerEmail, + orgId: org.id, + }); + }, + ); - it('allows a decision with no reason when the flag is off', async () => { - // Control case: default-off behavior must remain unchanged so orgs that - // never opt in see no difference from this PR. - await setRequiresDecisionReason(false); + // Issue #757: an IGNORE decision is gated by the ignore flag, not the + // action flag. With only the ignore flag on, an IGNORE with no reason is + // rejected. + testWithQueue( + 'rejects an IGNORE decision with no reason when only the ignore flag is on', + async ({ mrtService, org, queue }) => { + await configureDecisionReasonRequirements(mrtService, org.id, { + onAction: false, + onIgnore: true, + }); - const reviewerId = uuidv1(); - const reviewerEmail = 'test@test.com'; - const jobPayload = makeDummyJob(); - const itemId = jobPayload.payload.item.itemId; - const itemTypeId = jobPayload.payload.item.itemTypeIdentifier.id; + const reviewerId = uuidv1(); + const reviewerEmail = 'test@test.com'; + const jobPayload = makeDummyJob(); - await mrtService['queueOps']['addJob']({ - jobPayload, - orgId, - queueId, - enqueueSourceInfo: { kind: 'REPORT' }, - }); + await mrtService['queueOps']['addJob']({ + jobPayload, + orgId: org.id, + queueId: queue.id, + enqueueSourceInfo: { kind: 'REPORT' }, + }); - const dequeuedJob = await mrtService.dequeueNextJob({ - orgId, - queueId, - userId: reviewerId, - }); + const dequeuedJob = await mrtService.dequeueNextJob({ + orgId: org.id, + queueId: queue.id, + userId: reviewerId, + }); - if (!dequeuedJob) { - throw new Error("should've returned a job"); - } + if (!dequeuedJob) { + throw new Error("should've returned a job"); + } + + await expect( + mrtService.submitDecision({ + queueId: queue.id, + reportHistory: [], + jobId: dequeuedJob.job.id, + lockToken: dequeuedJob.lockToken, + decisionComponents: [{ type: 'IGNORE' }], + relatedActions: [], + reviewerId, + reviewerEmail, + orgId: org.id, + // decisionReason intentionally omitted + }), + ).rejects.toThrow(/requires every decision to include a reason/i); + }, + ); - await mrtService.submitDecision({ - queueId, - reportHistory: [], - jobId: dequeuedJob.job.id, - lockToken: dequeuedJob.lockToken, - decisionComponents: [ - { - type: 'CUSTOM_ACTION', - actions: [{ id: seededActionId }], - policies: [{ id: uuidv1() }], - itemIds: [itemId], - itemTypeId, - }, - ], - relatedActions: [], - reviewerId, - reviewerEmail, - orgId, - }); - }); - - // Issue #757: an IGNORE decision is gated by the ignore flag, not the - // action flag. With only the ignore flag on, an IGNORE with no reason is - // rejected. - it('rejects an IGNORE decision with no reason when only the ignore flag is on', async () => { - await setRequiresDecisionReason(false); - await setRequiresDecisionReasonOnIgnore(true); + // Issue #757: with only the action flag on, ignoring a job must NOT require + // a reason — this is the bug from the issue. + testWithQueue( + 'allows an IGNORE decision with no reason when only the action flag is on', + async ({ mrtService, org, queue }) => { + await configureDecisionReasonRequirements(mrtService, org.id, { + onAction: true, + onIgnore: false, + }); - const reviewerId = uuidv1(); - const reviewerEmail = 'test@test.com'; - const jobPayload = makeDummyJob(); + const reviewerId = uuidv1(); + const reviewerEmail = 'test@test.com'; + const jobPayload = makeDummyJob(); - await mrtService['queueOps']['addJob']({ - jobPayload, - orgId, - queueId, - enqueueSourceInfo: { kind: 'REPORT' }, - }); + await mrtService['queueOps']['addJob']({ + jobPayload, + orgId: org.id, + queueId: queue.id, + enqueueSourceInfo: { kind: 'REPORT' }, + }); - const dequeuedJob = await mrtService.dequeueNextJob({ - orgId, - queueId, - userId: reviewerId, - }); + const dequeuedJob = await mrtService.dequeueNextJob({ + orgId: org.id, + queueId: queue.id, + userId: reviewerId, + }); - if (!dequeuedJob) { - throw new Error("should've returned a job"); - } + if (!dequeuedJob) { + throw new Error("should've returned a job"); + } - await expect( - mrtService.submitDecision({ - queueId, + await mrtService.submitDecision({ + queueId: queue.id, reportHistory: [], jobId: dequeuedJob.job.id, lockToken: dequeuedJob.lockToken, @@ -569,224 +657,223 @@ describe('Manual Review Tool Service', () => { relatedActions: [], reviewerId, reviewerEmail, - orgId, + orgId: org.id, // decisionReason intentionally omitted - }), - ).rejects.toThrow(/requires every decision to include a reason/i); - }); - - // Issue #757: with only the action flag on, ignoring a job must NOT require - // a reason — this is the bug from the issue. - it('allows an IGNORE decision with no reason when only the action flag is on', async () => { - await setRequiresDecisionReason(true); - await setRequiresDecisionReasonOnIgnore(false); - - const reviewerId = uuidv1(); - const reviewerEmail = 'test@test.com'; - const jobPayload = makeDummyJob(); - - await mrtService['queueOps']['addJob']({ - jobPayload, - orgId, - queueId, - enqueueSourceInfo: { kind: 'REPORT' }, - }); - - const dequeuedJob = await mrtService.dequeueNextJob({ - orgId, - queueId, - userId: reviewerId, - }); - - if (!dequeuedJob) { - throw new Error("should've returned a job"); - } - - await mrtService.submitDecision({ - queueId, - reportHistory: [], - jobId: dequeuedJob.job.id, - lockToken: dequeuedJob.lockToken, - decisionComponents: [{ type: 'IGNORE' }], - relatedActions: [], - reviewerId, - reviewerEmail, - orgId, - // decisionReason intentionally omitted - }); - }); + }); + }, + ); // Issue #757: with only the ignore flag on, acting on a violating job must // NOT require a reason. - it('allows a CUSTOM_ACTION decision with no reason when only the ignore flag is on', async () => { - await setRequiresDecisionReason(false); - await setRequiresDecisionReasonOnIgnore(true); + testWithQueue( + 'allows a CUSTOM_ACTION decision with no reason when only the ignore flag is on', + async ({ mrtService, org, queue, actionId }) => { + await configureDecisionReasonRequirements(mrtService, org.id, { + onAction: false, + onIgnore: true, + }); - const reviewerId = uuidv1(); - const reviewerEmail = 'test@test.com'; - const jobPayload = makeDummyJob(); - const itemId = jobPayload.payload.item.itemId; - const itemTypeId = jobPayload.payload.item.itemTypeIdentifier.id; + const reviewerId = uuidv1(); + const reviewerEmail = 'test@test.com'; + const jobPayload = makeDummyJob(); + const itemId = jobPayload.payload.item.itemId; + const itemTypeId = jobPayload.payload.item.itemTypeIdentifier.id; - await mrtService['queueOps']['addJob']({ - jobPayload, - orgId, - queueId, - enqueueSourceInfo: { kind: 'REPORT' }, - }); + await mrtService['queueOps']['addJob']({ + jobPayload, + orgId: org.id, + queueId: queue.id, + enqueueSourceInfo: { kind: 'REPORT' }, + }); - const dequeuedJob = await mrtService.dequeueNextJob({ - orgId, - queueId, - userId: reviewerId, - }); + const dequeuedJob = await mrtService.dequeueNextJob({ + orgId: org.id, + queueId: queue.id, + userId: reviewerId, + }); - if (!dequeuedJob) { - throw new Error("should've returned a job"); - } + if (!dequeuedJob) { + throw new Error("should've returned a job"); + } - await mrtService.submitDecision({ - queueId, - reportHistory: [], - jobId: dequeuedJob.job.id, - lockToken: dequeuedJob.lockToken, - decisionComponents: [ - { - type: 'CUSTOM_ACTION', - actions: [{ id: seededActionId }], - policies: [{ id: uuidv1() }], - itemIds: [itemId], - itemTypeId, - }, - ], - relatedActions: [], - reviewerId, - reviewerEmail, - orgId, - // decisionReason intentionally omitted - }); - }); + await mrtService.submitDecision({ + queueId: queue.id, + reportHistory: [], + jobId: dequeuedJob.job.id, + lockToken: dequeuedJob.lockToken, + decisionComponents: [ + { + type: 'CUSTOM_ACTION', + actions: [{ id: actionId }], + policies: [{ id: uuidv1() }], + itemIds: [itemId], + itemTypeId, + }, + ], + relatedActions: [], + reviewerId, + reviewerEmail, + orgId: org.id, + // decisionReason intentionally omitted + }); + }, + ); // Issue #736: NCMEC review uses Submit NCMEC Report or Ignore, neither of // which carries a written decision reason. The require-reason flag is for // moderation decisions on standard MRT jobs and should not block the // NCMEC path. - it('allows an IGNORE decision on an NCMEC job with no reason when the flag is on', async () => { - await setRequiresDecisionReason(true); - await setRequiresDecisionReasonOnIgnore(true); + testWithQueue( + 'allows an IGNORE decision on an NCMEC job with no reason when the flag is on', + async ({ mrtService, org, queue }) => { + await configureDecisionReasonRequirements(mrtService, org.id, { + onAction: true, + onIgnore: true, + }); - const reviewerId = uuidv1(); - const reviewerEmail = 'test@test.com'; - const jobPayload = makeDummyNcmecJob(); + const reviewerId = uuidv1(); + const reviewerEmail = 'test@test.com'; + const jobPayload = makeDummyNcmecJob(); - await mrtService['queueOps']['addJob']({ - jobPayload, - orgId, - queueId, - enqueueSourceInfo: { kind: 'REPORT' }, - }); + await mrtService['queueOps']['addJob']({ + jobPayload, + orgId: org.id, + queueId: queue.id, + enqueueSourceInfo: { kind: 'REPORT' }, + }); - const dequeuedJob = await mrtService.dequeueNextJob({ - orgId, - queueId, - userId: reviewerId, - }); + const dequeuedJob = await mrtService.dequeueNextJob({ + orgId: org.id, + queueId: queue.id, + userId: reviewerId, + }); - if (!dequeuedJob) { - throw new Error("should've returned a job"); - } + if (!dequeuedJob) { + throw new Error("should've returned a job"); + } - await mrtService.submitDecision({ - queueId, - reportHistory: [], - jobId: dequeuedJob.job.id, - lockToken: dequeuedJob.lockToken, - decisionComponents: [{ type: 'IGNORE' }], - relatedActions: [], - reviewerId, - reviewerEmail, - orgId, - // decisionReason intentionally omitted - }); - }); + await mrtService.submitDecision({ + queueId: queue.id, + reportHistory: [], + jobId: dequeuedJob.job.id, + lockToken: dequeuedJob.lockToken, + decisionComponents: [{ type: 'IGNORE' }], + relatedActions: [], + reviewerId, + reviewerEmail, + orgId: org.id, + // decisionReason intentionally omitted + }); + }, + ); }); // Issue #389: when an org sets `requires_policy_for_decisions`, submitDecision // must reject CUSTOM_ACTION decisions with no policies. The UI already blocks // this; the server-side check closes the API-bypass gap. describe('requires_policy_for_decisions enforcement', () => { - const orgId = 'e7c89ce7729'; - const queueId = '1'; - // Pulled from the staging seed data — any CUSTOM_ACTION row on this org - // will do; the action-id validation runs before our flag check. - const seededActionId = '1873b2f15cc'; - - const setRequiresPolicyForDecisions = async (value: boolean) => { - await mrtService.upsertDefaultSettings({ orgId }); - await mrtService['pgQuery'] - .updateTable('manual_review_tool.manual_review_tool_settings') - .set({ requires_policy_for_decisions: value }) - .where('org_id', '=', orgId) - .execute(); - }; - - beforeAll(async () => { - await mrtService['pgQuery'] - .insertInto('manual_review_tool.manual_review_queues') - .values({ - id: queueId, - name: 'integ-test-queue', - description: null, - org_id: orgId, - is_default_queue: false, - is_appeals_queue: false, - auto_close_jobs: false, - }) - .onConflict((oc) => oc.doNothing()) - .execute(); - }); - - afterEach(async () => { - await setRequiresPolicyForDecisions(false); - }); + testWithQueue( + 'rejects a CUSTOM_ACTION decision with no policies when the flag is on', + async ({ mrtService, deps, org, queue, actionId }) => { + await setRequiresPolicyForDecisions( + mrtService, + deps.KyselyPg, + org.id, + true, + ); + + const reviewerId = uuidv1(); + const reviewerEmail = 'test@test.com'; + const jobPayload = makeDummyJob(); + const itemId = jobPayload.payload.item.itemId; + const itemTypeId = jobPayload.payload.item.itemTypeIdentifier.id; + + await mrtService['queueOps']['addJob']({ + jobPayload, + orgId: org.id, + queueId: queue.id, + enqueueSourceInfo: { kind: 'REPORT' }, + }); - it('rejects a CUSTOM_ACTION decision with no policies when the flag is on', async () => { - await setRequiresPolicyForDecisions(true); + const dequeuedJob = await mrtService.dequeueNextJob({ + orgId: org.id, + queueId: queue.id, + userId: reviewerId, + }); - const reviewerId = uuidv1(); - const reviewerEmail = 'test@test.com'; - const jobPayload = makeDummyJob(); - const itemId = jobPayload.payload.item.itemId; - const itemTypeId = jobPayload.payload.item.itemTypeIdentifier.id; + if (!dequeuedJob) { + throw new Error("should've returned a job"); + } + + await expect( + mrtService.submitDecision({ + queueId: queue.id, + reportHistory: [], + jobId: dequeuedJob.job.id, + lockToken: dequeuedJob.lockToken, + decisionComponents: [ + { + type: 'CUSTOM_ACTION', + actions: [{ id: actionId }], + policies: [], + itemIds: [itemId], + itemTypeId, + }, + ], + relatedActions: [], + reviewerId, + reviewerEmail, + orgId: org.id, + }), + ).rejects.toThrow( + /requires every decision to include at least one policy/i, + ); + }, + ); - await mrtService['queueOps']['addJob']({ - jobPayload, - orgId, - queueId, - enqueueSourceInfo: { kind: 'REPORT' }, - }); + testWithQueue( + 'allows a CUSTOM_ACTION decision with policies when the flag is on', + async ({ mrtService, deps, org, queue, actionId }) => { + await setRequiresPolicyForDecisions( + mrtService, + deps.KyselyPg, + org.id, + true, + ); + + const reviewerId = uuidv1(); + const reviewerEmail = 'test@test.com'; + const jobPayload = makeDummyJob(); + const itemId = jobPayload.payload.item.itemId; + const itemTypeId = jobPayload.payload.item.itemTypeIdentifier.id; + + await mrtService['queueOps']['addJob']({ + jobPayload, + orgId: org.id, + queueId: queue.id, + enqueueSourceInfo: { kind: 'REPORT' }, + }); - const dequeuedJob = await mrtService.dequeueNextJob({ - orgId, - queueId, - userId: reviewerId, - }); + const dequeuedJob = await mrtService.dequeueNextJob({ + orgId: org.id, + queueId: queue.id, + userId: reviewerId, + }); - if (!dequeuedJob) { - throw new Error("should've returned a job"); - } + if (!dequeuedJob) { + throw new Error("should've returned a job"); + } - await expect( - mrtService.submitDecision({ - queueId, + await mrtService.submitDecision({ + queueId: queue.id, reportHistory: [], jobId: dequeuedJob.job.id, lockToken: dequeuedJob.lockToken, decisionComponents: [ { type: 'CUSTOM_ACTION', - actions: [{ id: seededActionId }], - policies: [], + actions: [{ id: actionId }], + policies: [{ id: uuidv1() }], itemIds: [itemId], itemTypeId, }, @@ -794,145 +881,113 @@ describe('Manual Review Tool Service', () => { relatedActions: [], reviewerId, reviewerEmail, - orgId, - }), - ).rejects.toThrow( - /requires every decision to include at least one policy/i, - ); - }); - - it('allows a CUSTOM_ACTION decision with policies when the flag is on', async () => { - await setRequiresPolicyForDecisions(true); - - const reviewerId = uuidv1(); - const reviewerEmail = 'test@test.com'; - const jobPayload = makeDummyJob(); - const itemId = jobPayload.payload.item.itemId; - const itemTypeId = jobPayload.payload.item.itemTypeIdentifier.id; - - await mrtService['queueOps']['addJob']({ - jobPayload, - orgId, - queueId, - enqueueSourceInfo: { kind: 'REPORT' }, - }); - - const dequeuedJob = await mrtService.dequeueNextJob({ - orgId, - queueId, - userId: reviewerId, - }); - - if (!dequeuedJob) { - throw new Error("should've returned a job"); - } - - await mrtService.submitDecision({ - queueId, - reportHistory: [], - jobId: dequeuedJob.job.id, - lockToken: dequeuedJob.lockToken, - decisionComponents: [ - { - type: 'CUSTOM_ACTION', - actions: [{ id: seededActionId }], - policies: [{ id: uuidv1() }], - itemIds: [itemId], - itemTypeId, - }, - ], - relatedActions: [], - reviewerId, - reviewerEmail, - orgId, - }); - }); - - it('allows a CUSTOM_ACTION decision without policies when the flag is off', async () => { - await setRequiresPolicyForDecisions(false); - - const reviewerId = uuidv1(); - const reviewerEmail = 'test@test.com'; - const jobPayload = makeDummyJob(); - const itemId = jobPayload.payload.item.itemId; - const itemTypeId = jobPayload.payload.item.itemTypeIdentifier.id; + orgId: org.id, + }); + }, + ); - await mrtService['queueOps']['addJob']({ - jobPayload, - orgId, - queueId, - enqueueSourceInfo: { kind: 'REPORT' }, - }); + testWithQueue( + 'allows a CUSTOM_ACTION decision without policies when the flag is off', + async ({ mrtService, deps, org, queue, actionId }) => { + await setRequiresPolicyForDecisions( + mrtService, + deps.KyselyPg, + org.id, + false, + ); + + const reviewerId = uuidv1(); + const reviewerEmail = 'test@test.com'; + const jobPayload = makeDummyJob(); + const itemId = jobPayload.payload.item.itemId; + const itemTypeId = jobPayload.payload.item.itemTypeIdentifier.id; + + await mrtService['queueOps']['addJob']({ + jobPayload, + orgId: org.id, + queueId: queue.id, + enqueueSourceInfo: { kind: 'REPORT' }, + }); - const dequeuedJob = await mrtService.dequeueNextJob({ - orgId, - queueId, - userId: reviewerId, - }); + const dequeuedJob = await mrtService.dequeueNextJob({ + orgId: org.id, + queueId: queue.id, + userId: reviewerId, + }); - if (!dequeuedJob) { - throw new Error("should've returned a job"); - } + if (!dequeuedJob) { + throw new Error("should've returned a job"); + } - await mrtService.submitDecision({ - queueId, - reportHistory: [], - jobId: dequeuedJob.job.id, - lockToken: dequeuedJob.lockToken, - decisionComponents: [ - { - type: 'CUSTOM_ACTION', - actions: [{ id: seededActionId }], - policies: [], - itemIds: [itemId], - itemTypeId, - }, - ], - relatedActions: [], - reviewerId, - reviewerEmail, - orgId, - }); - }); + await mrtService.submitDecision({ + queueId: queue.id, + reportHistory: [], + jobId: dequeuedJob.job.id, + lockToken: dequeuedJob.lockToken, + decisionComponents: [ + { + type: 'CUSTOM_ACTION', + actions: [{ id: actionId }], + policies: [], + itemIds: [itemId], + itemTypeId, + }, + ], + relatedActions: [], + reviewerId, + reviewerEmail, + orgId: org.id, + }); + }, + ); }); // Issue #615: orgs created before manual_review_tool_settings existed have no // row, so a save against them used to UPDATE zero rows and silently no-op. describe('settings persistence without a pre-existing row', () => { - const orgId = `no-row-${uuidv1()}`; - - afterEach(async () => { - await mrtService['pgQuery'] - .deleteFrom('manual_review_tool.manual_review_tool_settings') - .where('org_id', '=', orgId) - .execute(); - }); - - it('persists a boolean toggle when the org has no settings row', async () => { - expect(await mrtService.getHideSkipButtonForNonAdmins(orgId)).toBe(false); - - await mrtService.updateHideSkipButtonForNonAdmins(orgId, true); - - expect(await mrtService.getHideSkipButtonForNonAdmins(orgId)).toBe(true); - }); - - it('persists the ignore callback url when the org has no settings row', async () => { - await mrtService.updateIgnoreCallbackUrl( - orgId, - 'https://example.com/webhook/ignore', - ); + testWithService( + 'persists a boolean toggle when the org has no settings row', + async ({ mrtService }) => { + const orgId = `no-row-${uid()}`; + expect(await mrtService.getHideSkipButtonForNonAdmins(orgId)).toBe( + false, + ); + + await mrtService.updateHideSkipButtonForNonAdmins(orgId, true); + + expect(await mrtService.getHideSkipButtonForNonAdmins(orgId)).toBe( + true, + ); + }, + ); - expect(await mrtService.getIgnoreCallbackUrl(orgId)).toBe( - 'https://example.com/webhook/ignore', - ); - }); + testWithService( + 'persists the ignore callback url when the org has no settings row', + async ({ mrtService }) => { + const orgId = `no-row-${uid()}`; + await mrtService.updateIgnoreCallbackUrl( + orgId, + 'https://example.com/webhook/ignore', + ); - it('leaves other columns at their defaults when upserting one setting', async () => { - await mrtService.updatePreviewJobsViewEnabled(orgId, true); + expect(await mrtService.getIgnoreCallbackUrl(orgId)).toBe( + 'https://example.com/webhook/ignore', + ); + }, + ); - expect(await mrtService.getPreviewJobsViewEnabled(orgId)).toBe(true); - expect(await mrtService.getRequiresPolicyForDecisions(orgId)).toBe(false); - expect(await mrtService.getRequiresDecisionReason(orgId)).toBe(false); - }); + testWithService( + 'leaves other columns at their defaults when upserting one setting', + async ({ mrtService }) => { + const orgId = `no-row-${uid()}`; + await mrtService.updatePreviewJobsViewEnabled(orgId, true); + + expect(await mrtService.getPreviewJobsViewEnabled(orgId)).toBe(true); + expect(await mrtService.getRequiresPolicyForDecisions(orgId)).toBe( + false, + ); + expect(await mrtService.getRequiresDecisionReason(orgId)).toBe(false); + }, + ); }); }); diff --git a/server/services/manualReviewToolService/modules/CommentOperations.test.ts b/server/services/manualReviewToolService/modules/CommentOperations.test.ts index dd17ef92..7e1801ef 100644 --- a/server/services/manualReviewToolService/modules/CommentOperations.test.ts +++ b/server/services/manualReviewToolService/modules/CommentOperations.test.ts @@ -1,39 +1,34 @@ import { v1 as uuidv1 } from 'uuid'; -import getBottle from '../../../iocContainer/index.js'; import createOrg from '../../../test/fixtureHelpers/createOrg.js'; import createUser from '../../../test/fixtureHelpers/createUser.js'; -import { makeTestWithFixture } from '../../../test/utils.js'; +import { makeTransactionalTestWithFixture } from '../../../test/harness/transactionalTest.js'; import { UserPermission } from '../../userManagementService/index.js'; import { type JobId } from '../manualReviewToolService.js'; import CommentOperations from './CommentOperations.js'; describe('CommentOperations', () => { - const testWithFixtures = makeTestWithFixture(async () => { - const container = (await getBottle()).container; - const pgQuery = container.KyselyPg; - const commentOps = new CommentOperations(pgQuery); - - // Create test org - const orgId = uuidv1(); - const { cleanup: orgCleanup } = await createOrg( - { - KyselyPg: container.KyselyPg, - ModerationConfigService: container.ModerationConfigService, - ApiKeyService: container.ApiKeyService, - }, - orgId, - ); + const testWithFixtures = makeTransactionalTestWithFixture( + async ({ deps }) => { + const pgQuery = deps.KyselyPg; + const commentOps = new CommentOperations(pgQuery); + + // Create test org + const orgId = uuidv1(); + await createOrg( + { + KyselyPg: deps.KyselyPg, + ModerationConfigService: deps.ModerationConfigService, + ApiKeyService: deps.ApiKeyService, + }, + orgId, + ); - // Create test user - const { user, cleanup: userCleanup } = await createUser( - container.KyselyPg, - orgId, - ); + // Create test user + const { user } = await createUser(deps.KyselyPg, orgId); - // Create a queue (required for job_creations foreign key) - const queue = - await container.ManualReviewToolService.createManualReviewQueue({ + // Create a queue (required for job_creations foreign key) + const queue = await deps.ManualReviewToolService.createManualReviewQueue({ name: 'Test Queue', description: null, userIds: [user.id], @@ -46,75 +41,49 @@ describe('CommentOperations', () => { }, }); - // Create test item identifiers and jobs - const itemId = uuidv1(); - const itemTypeId = uuidv1(); - const jobId1 = uuidv1(); - const jobId2 = uuidv1(); - - await pgQuery - .insertInto('manual_review_tool.job_creations') - .values([ - { - id: jobId1 as JobId, - org_id: orgId, - item_id: itemId, - item_type_id: itemTypeId, - queue_id: queue.id, - created_at: new Date('2023-01-01'), - enqueue_source_info: {}, - }, - { - id: jobId2 as JobId, - org_id: orgId, - item_id: itemId, - item_type_id: itemTypeId, - queue_id: queue.id, - created_at: new Date('2023-01-02'), - enqueue_source_info: {}, - }, - ]) - .execute(); - - return { - commentOps, - pgQuery, - orgId, - userId: user.id, - itemId, - itemTypeId, - jobId1, - jobId2, - queueId: queue.id, - async cleanup() { - // Clean up comments - await pgQuery - .deleteFrom('manual_review_tool.job_comments') - .where('org_id', '=', orgId) - .execute(); - - // Clean up job_creations - await pgQuery - .deleteFrom('manual_review_tool.job_creations') - .where('org_id', '=', orgId) - .execute(); - - // Clean up queue - await container.ManualReviewToolService.deleteManualReviewQueueForTestsDO_NOT_USE( - orgId, - queue.id, - ); - - // Clean up user and org - await userCleanup(); - await orgCleanup(); - - // Close database connections - await container.KyselyPg.destroy(); - await container.KyselyPgReadReplica.destroy(); - }, - }; - }); + // Create test item identifiers and jobs + const itemId = uuidv1(); + const itemTypeId = uuidv1(); + const jobId1 = uuidv1(); + const jobId2 = uuidv1(); + + await pgQuery + .insertInto('manual_review_tool.job_creations') + .values([ + { + id: jobId1 as JobId, + org_id: orgId, + item_id: itemId, + item_type_id: itemTypeId, + queue_id: queue.id, + created_at: new Date('2023-01-01'), + enqueue_source_info: {}, + }, + { + id: jobId2 as JobId, + org_id: orgId, + item_id: itemId, + item_type_id: itemTypeId, + queue_id: queue.id, + created_at: new Date('2023-01-02'), + enqueue_source_info: {}, + }, + ]) + .execute(); + + return { + commentOps, + pgQuery, + orgId, + userId: user.id, + itemId, + itemTypeId, + jobId1, + jobId2, + queueId: queue.id, + }; + }, + ); describe('getRelatedJobIds', () => { testWithFixtures( diff --git a/server/services/manualReviewToolService/modules/JobRouting.test.ts b/server/services/manualReviewToolService/modules/JobRouting.test.ts index 30c01d08..6b419307 100644 --- a/server/services/manualReviewToolService/modules/JobRouting.test.ts +++ b/server/services/manualReviewToolService/modules/JobRouting.test.ts @@ -2,11 +2,10 @@ import { ScalarTypes } from '@roostorg/coop-types'; import { uid } from 'uid'; -import getBottle from '../../../iocContainer/index.js'; import createContentItemTypes from '../../../test/fixtureHelpers/createContentItemTypes.js'; import createOrg from '../../../test/fixtureHelpers/createOrg.js'; import createUser from '../../../test/fixtureHelpers/createUser.js'; -import { makeTestWithFixture } from '../../../test/utils.js'; +import { makeTransactionalTestWithFixture } from '../../../test/harness/transactionalTest.js'; import { toCorrelationId } from '../../../utils/correlationIds.js'; import { jsonStringify } from '../../../utils/encoding.js'; import { type NonEmptyString } from '../../../utils/typescript-types.js'; @@ -20,25 +19,21 @@ import { SignalType } from '../../signalsService/index.js'; import { UserPermission } from '../../userManagementService/index.js'; describe('JobRouting tests', () => { - const jobRoutingTestWithFixtures = makeTestWithFixture(async () => { - const { container } = await getBottle(); - const manualReviewToolService = container.ManualReviewToolService; - const { org, cleanup: orgCleanup } = await createOrg( - { - KyselyPg: container.KyselyPg, - ModerationConfigService: container.ModerationConfigService, - ApiKeyService: container.ApiKeyService, - }, - uid(), - ); - const { user, cleanup: userCleanup } = await createUser( - container.KyselyPg, - org.id, - ); - const userId = user.id; - const { itemTypes, cleanup: itemTypesCleanup } = - await createContentItemTypes({ - moderationConfigService: container.ModerationConfigService, + const jobRoutingTestWithFixtures = makeTransactionalTestWithFixture( + async ({ deps }) => { + const manualReviewToolService = deps.ManualReviewToolService; + const { org } = await createOrg( + { + KyselyPg: deps.KyselyPg, + ModerationConfigService: deps.ModerationConfigService, + ApiKeyService: deps.ApiKeyService, + }, + uid(), + ); + const { user } = await createUser(deps.KyselyPg, org.id); + const userId = user.id; + const { itemTypes } = await createContentItemTypes({ + moderationConfigService: deps.ModerationConfigService, orgId: org.id, extra: { fields: [ @@ -51,110 +46,112 @@ describe('JobRouting tests', () => { ], }, }); - const itemType = itemTypes[0]; - - const defaultQueue = await manualReviewToolService.createManualReviewQueue({ - name: 'Default Queue', - description: null, - userIds: [userId], - hiddenActionIds: [], - isAppealsQueue: false, - invokedBy: { - userId, - permissions: [UserPermission.EDIT_MRT_QUEUES], - orgId: org.id, - }, - }); - const anotherQueue = await manualReviewToolService.createManualReviewQueue({ - name: 'Another Queue', - description: null, - userIds: [userId], - hiddenActionIds: [], - isAppealsQueue: false, - invokedBy: { - userId, - permissions: [UserPermission.EDIT_MRT_QUEUES], - orgId: org.id, - }, - }); - const policyQueue = await manualReviewToolService.createManualReviewQueue({ - name: 'Policy Queue', - description: null, - userIds: [userId], - hiddenActionIds: [], - isAppealsQueue: false, - invokedBy: { - userId, - permissions: [UserPermission.EDIT_MRT_QUEUES], - orgId: org.id, - }, - }); - const noPolicyQueue = await manualReviewToolService.createManualReviewQueue( - { - name: 'No Policy Queue', - description: null, - userIds: [userId], - hiddenActionIds: [], - isAppealsQueue: false, - invokedBy: { - userId, - permissions: [UserPermission.EDIT_MRT_QUEUES], - orgId: org.id, + const itemType = itemTypes[0]; + + const defaultQueue = + await manualReviewToolService.createManualReviewQueue({ + name: 'Default Queue', + description: null, + userIds: [userId], + hiddenActionIds: [], + isAppealsQueue: false, + invokedBy: { + userId, + permissions: [UserPermission.EDIT_MRT_QUEUES], + orgId: org.id, + }, + }); + const anotherQueue = + await manualReviewToolService.createManualReviewQueue({ + name: 'Another Queue', + description: null, + userIds: [userId], + hiddenActionIds: [], + isAppealsQueue: false, + invokedBy: { + userId, + permissions: [UserPermission.EDIT_MRT_QUEUES], + orgId: org.id, + }, + }); + const policyQueue = await manualReviewToolService.createManualReviewQueue( + { + name: 'Policy Queue', + description: null, + userIds: [userId], + hiddenActionIds: [], + isAppealsQueue: false, + invokedBy: { + userId, + permissions: [UserPermission.EDIT_MRT_QUEUES], + orgId: org.id, + }, }, - }, - ); - - const rule = await manualReviewToolService.createRoutingRule({ - orgId: org.id, - name: 'Some rule', - status: 'LIVE', - itemTypeIds: [itemType.id as NonEmptyString], - creatorId: '', - conditionSet: { - conjunction: 'AND', - conditions: [ - { - input: { - type: 'CONTENT_FIELD', - name: 'text', - contentTypeId: itemType.id, - }, - signal: { - id: jsonStringify({ + ); + const noPolicyQueue = + await manualReviewToolService.createManualReviewQueue({ + name: 'No Policy Queue', + description: null, + userIds: [userId], + hiddenActionIds: [], + isAppealsQueue: false, + invokedBy: { + userId, + permissions: [UserPermission.EDIT_MRT_QUEUES], + orgId: org.id, + }, + }); + + await manualReviewToolService.createRoutingRule({ + orgId: org.id, + name: 'Some rule', + status: 'LIVE', + itemTypeIds: [itemType.id as NonEmptyString], + creatorId: '', + conditionSet: { + conjunction: 'AND', + conditions: [ + { + input: { + type: 'CONTENT_FIELD', + name: 'text', + contentTypeId: itemType.id, + }, + signal: { + id: jsonStringify({ + type: SignalType.TEXT_MATCHING_CONTAINS_TEXT, + }), type: SignalType.TEXT_MATCHING_CONTAINS_TEXT, - }), - type: SignalType.TEXT_MATCHING_CONTAINS_TEXT, + }, + matchingValues: { strings: ['test'] }, }, - matchingValues: { strings: ['test'] }, - }, - ], - }, - destinationQueueId: anotherQueue.id, - }); - - const policyRule = await manualReviewToolService.createRoutingRule({ - orgId: org.id, - name: 'Policy ID rule', - status: 'LIVE', - itemTypeIds: [itemType.id as NonEmptyString], - creatorId: '', - conditionSet: { - conjunction: 'OR', - conditions: [ - { - input: { - type: 'CONTENT_COOP_INPUT', - name: 'Relevant Policy', + ], + }, + destinationQueueId: anotherQueue.id, + }); + + await manualReviewToolService.createRoutingRule({ + orgId: org.id, + name: 'Policy ID rule', + status: 'LIVE', + itemTypeIds: [itemType.id as NonEmptyString], + creatorId: '', + conditionSet: { + conjunction: 'OR', + conditions: [ + { + input: { + type: 'CONTENT_COOP_INPUT', + name: 'Relevant Policy', + }, + threshold: 'testPolicyId', + comparator: 'EQUALS', }, - threshold: 'testPolicyId', - comparator: 'EQUALS', - }, - ], - }, - destinationQueueId: policyQueue.id, - }); + ], + }, + destinationQueueId: policyQueue.id, + }); - const policyNotProvidedRule = await manualReviewToolService.createRoutingRule({ orgId: org.id, name: 'Policy ID not provided rule', @@ -190,76 +187,39 @@ describe('JobRouting tests', () => { destinationQueueId: noPolicyQueue.id, }); - const sourceTypeRule = await manualReviewToolService.createRoutingRule({ - orgId: org.id, - name: 'Source Type rule', - status: 'LIVE', - itemTypeIds: [itemType.id as NonEmptyString], - creatorId: '', - conditionSet: { - conjunction: 'OR', - conditions: [ - { - input: { - type: 'CONTENT_COOP_INPUT', - name: 'Source', + await manualReviewToolService.createRoutingRule({ + orgId: org.id, + name: 'Source Type rule', + status: 'LIVE', + itemTypeIds: [itemType.id as NonEmptyString], + creatorId: '', + conditionSet: { + conjunction: 'OR', + conditions: [ + { + input: { + type: 'CONTENT_COOP_INPUT', + name: 'Source', + }, + threshold: 'post-actions', + comparator: 'EQUALS', }, - threshold: 'post-actions', - comparator: 'EQUALS', - }, - ], - }, - destinationQueueId: anotherQueue.id, - }); + ], + }, + destinationQueueId: anotherQueue.id, + }); - return { - manualReviewToolService, - org, - itemType, - defaultQueue, - anotherQueue, - policyQueue, - noPolicyQueue, - async cleanup() { - await manualReviewToolService.deleteRoutingRule({ - id: rule.id, - orgId: org.id, - }); - await manualReviewToolService.deleteRoutingRule({ - id: policyRule.id, - orgId: org.id, - }); - await manualReviewToolService.deleteRoutingRule({ - id: policyNotProvidedRule.id, - orgId: org.id, - }); - await manualReviewToolService.deleteRoutingRule({ - id: sourceTypeRule.id, - orgId: org.id, - }); - await manualReviewToolService.deleteManualReviewQueueForTestsDO_NOT_USE( - org.id, - anotherQueue.id, - ); - await manualReviewToolService.deleteManualReviewQueueForTestsDO_NOT_USE( - org.id, - policyQueue.id, - ); - await manualReviewToolService.deleteManualReviewQueueForTestsDO_NOT_USE( - org.id, - defaultQueue.id, - ); - await manualReviewToolService.deleteManualReviewQueueForTestsDO_NOT_USE( - org.id, - noPolicyQueue.id, - ); - await itemTypesCleanup(); - await userCleanup(); - await orgCleanup(); - await container.closeSharedResourcesForShutdown(); - }, - }; - }); + return { + manualReviewToolService, + org, + itemType, + defaultQueue, + anotherQueue, + policyQueue, + noPolicyQueue, + }; + }, + ); jobRoutingTestWithFixtures( 'Should enqueue based off of routing rule', diff --git a/server/services/manualReviewToolService/modules/QueueOperations.test.ts b/server/services/manualReviewToolService/modules/QueueOperations.test.ts index 24dd8873..182d4edc 100644 --- a/server/services/manualReviewToolService/modules/QueueOperations.test.ts +++ b/server/services/manualReviewToolService/modules/QueueOperations.test.ts @@ -1,14 +1,12 @@ import fc from 'fast-check'; import { uid } from 'uid'; -import getBottle from '../../../iocContainer/index.js'; import createActions from '../../../test/fixtureHelpers/createActions.js'; import createContentItemTypes from '../../../test/fixtureHelpers/createContentItemTypes.js'; import createMrtQueue from '../../../test/fixtureHelpers/createMrtQueue.js'; import createOrg from '../../../test/fixtureHelpers/createOrg.js'; import createUser from '../../../test/fixtureHelpers/createUser.js'; import { makeTransactionalTestWithFixture } from '../../../test/harness/transactionalTest.js'; -import { makeTestWithFixture } from '../../../test/utils.js'; import { UserPermission } from '../../userManagementService/index.js'; import { bullJobIdtoExternalJobId, @@ -34,48 +32,42 @@ describe('QueueOperations', () => { }); const testWithQueueAndActions = () => - makeTestWithFixture(async () => { - const container = (await getBottle()).container; - - const { org, cleanup: orgCleanup } = await createOrg( + makeTransactionalTestWithFixture(async ({ deps }) => { + const { org } = await createOrg( { - KyselyPg: container.KyselyPg, - ModerationConfigService: container.ModerationConfigService, - ApiKeyService: container.ApiKeyService, + KyselyPg: deps.KyselyPg, + ModerationConfigService: deps.ModerationConfigService, + ApiKeyService: deps.ApiKeyService, }, uid(), ); - const { user, cleanup: userCleanup } = await createUser( - container.KyselyPg, - org.id, - ); - const { itemTypes, cleanup: itemTypesCleanup } = - await createContentItemTypes({ - moderationConfigService: container.ModerationConfigService, - orgId: org.id, - extra: { - fields: [ - { - name: 'someField', - type: 'NUMBER', - required: false, - container: null, - }, - ], - }, - }); + const { user } = await createUser(deps.KyselyPg, org.id); + const { itemTypes } = await createContentItemTypes({ + moderationConfigService: deps.ModerationConfigService, + orgId: org.id, + extra: { + fields: [ + { + name: 'someField', + type: 'NUMBER', + required: false, + container: null, + }, + ], + }, + }); - const { actions, cleanup: actionsCleanup } = await createActions({ - actionAPI: container.ActionAPIDataSource, + const { actions } = await createActions({ + actionAPI: deps.ActionAPIDataSource, itemTypeIds: itemTypes.map((it) => it.id), orgId: org.id, numActions: 3, }); - const { queue, cleanup: queuesCleanup } = await createMrtQueue({ + const { queue } = await createMrtQueue({ orgId: org.id, - mrtService: container.ManualReviewToolService, + mrtService: deps.ManualReviewToolService, userId: user.id, }); @@ -83,16 +75,7 @@ describe('QueueOperations', () => { org, actions, queue, - mrtService: container.ManualReviewToolService, - cleanup: async () => { - await queuesCleanup(); - await actionsCleanup(); - await itemTypesCleanup(); - await userCleanup(); - await orgCleanup(); - await container.KyselyPg.destroy(); - await container.KyselyPgReadReplica.destroy(); - }, + mrtService: deps.ManualReviewToolService, }; }); diff --git a/server/services/manualReviewToolService/modules/ReporterInvalidation.test.ts b/server/services/manualReviewToolService/modules/ReporterInvalidation.test.ts index 1bc40088..df8d7e2a 100644 --- a/server/services/manualReviewToolService/modules/ReporterInvalidation.test.ts +++ b/server/services/manualReviewToolService/modules/ReporterInvalidation.test.ts @@ -2,12 +2,11 @@ import { uid } from 'uid'; import { v1 as uuidv1 } from 'uuid'; -import getBottle from '../../../iocContainer/index.js'; import createContentItemTypes from '../../../test/fixtureHelpers/createContentItemTypes.js'; import createMrtQueue from '../../../test/fixtureHelpers/createMrtQueue.js'; import createOrg from '../../../test/fixtureHelpers/createOrg.js'; import createUser from '../../../test/fixtureHelpers/createUser.js'; -import { makeTestWithFixture } from '../../../test/utils.js'; +import { makeTransactionalTestWithFixture } from '../../../test/harness/transactionalTest.js'; import { instantiateOpaqueType } from '../../../utils/typescript-types.js'; import { makeSubmissionId, @@ -274,33 +273,28 @@ describe('scrubPayloadForReporter (pure)', () => { // Integration tests below mirror the pattern in `QueueOperations.test.ts`. const testWithQueue = () => - makeTestWithFixture(async () => { - const container = (await getBottle()).container; - const { org, cleanup: orgCleanup } = await createOrg( + makeTransactionalTestWithFixture(async ({ deps }) => { + const { org } = await createOrg( { - KyselyPg: container.KyselyPg, - ModerationConfigService: container.ModerationConfigService, - ApiKeyService: container.ApiKeyService, + KyselyPg: deps.KyselyPg, + ModerationConfigService: deps.ModerationConfigService, + ApiKeyService: deps.ApiKeyService, }, uid(), ); - const { user, cleanup: userCleanup } = await createUser( - container.KyselyPg, - org.id, - ); - const { itemTypes, cleanup: itemTypesCleanup } = - await createContentItemTypes({ - moderationConfigService: container.ModerationConfigService, - orgId: org.id, - extra: {}, - }); - const { queue, cleanup: queueCleanup } = await createMrtQueue({ + const { user } = await createUser(deps.KyselyPg, org.id); + const { itemTypes } = await createContentItemTypes({ + moderationConfigService: deps.ModerationConfigService, orgId: org.id, - mrtService: container.ManualReviewToolService, + extra: {}, + }); + const { queue } = await createMrtQueue({ + orgId: org.id, + mrtService: deps.ManualReviewToolService, userId: user.id, }); - const mrtService = container.ManualReviewToolService; + const mrtService = deps.ManualReviewToolService; // Bracket-index into the private QueueOperations to seed jobs directly, // matching the pattern in manualReviewToolService.test.ts. @@ -349,14 +343,6 @@ const testWithQueue = () => queue, mrtService, addJob, - cleanup: async () => { - await queueCleanup(); - await itemTypesCleanup(); - await userCleanup(); - await orgCleanup(); - await container.KyselyPg.destroy(); - await container.KyselyPgReadReplica.destroy(); - }, }; }); diff --git a/server/services/manualReviewToolService/modules/UserReportSweep.test.ts b/server/services/manualReviewToolService/modules/UserReportSweep.test.ts index 0e95cb16..68a8ded1 100644 --- a/server/services/manualReviewToolService/modules/UserReportSweep.test.ts +++ b/server/services/manualReviewToolService/modules/UserReportSweep.test.ts @@ -2,12 +2,11 @@ import { type ItemIdentifier } from '@roostorg/types'; import { uid } from 'uid'; import { v1 as uuidv1 } from 'uuid'; -import getBottle from '../../../iocContainer/index.js'; import createContentItemTypes from '../../../test/fixtureHelpers/createContentItemTypes.js'; import createMrtQueue from '../../../test/fixtureHelpers/createMrtQueue.js'; import createOrg from '../../../test/fixtureHelpers/createOrg.js'; import createUser from '../../../test/fixtureHelpers/createUser.js'; -import { makeTestWithFixture } from '../../../test/utils.js'; +import { makeTransactionalTestWithFixture } from '../../../test/harness/transactionalTest.js'; import { instantiateOpaqueType } from '../../../utils/typescript-types.js'; import { makeSubmissionId, @@ -19,33 +18,28 @@ import { type CustomActionDecisionComponent } from './JobDecisioning.js'; const TRIGGER_ACTION_ID = 'ban-action'; const testWithQueue = () => - makeTestWithFixture(async () => { - const container = (await getBottle()).container; - const { org, cleanup: orgCleanup } = await createOrg( + makeTransactionalTestWithFixture(async ({ deps }) => { + const { org } = await createOrg( { - KyselyPg: container.KyselyPg, - ModerationConfigService: container.ModerationConfigService, - ApiKeyService: container.ApiKeyService, + KyselyPg: deps.KyselyPg, + ModerationConfigService: deps.ModerationConfigService, + ApiKeyService: deps.ApiKeyService, }, uid(), ); - const { user, cleanup: userCleanup } = await createUser( - container.KyselyPg, - org.id, - ); - const { itemTypes, cleanup: itemTypesCleanup } = - await createContentItemTypes({ - moderationConfigService: container.ModerationConfigService, - orgId: org.id, - extra: {}, - }); - const { queue, cleanup: queueCleanup } = await createMrtQueue({ + const { user } = await createUser(deps.KyselyPg, org.id); + const { itemTypes } = await createContentItemTypes({ + moderationConfigService: deps.ModerationConfigService, orgId: org.id, - mrtService: container.ManualReviewToolService, + extra: {}, + }); + const { queue } = await createMrtQueue({ + orgId: org.id, + mrtService: deps.ManualReviewToolService, userId: user.id, }); - const mrtService = container.ManualReviewToolService; + const mrtService = deps.ManualReviewToolService; const queueOps = mrtService['queueOps']; const addJob = async (opts: { @@ -122,14 +116,6 @@ const testWithQueue = () => addJob, configureQueue, pendingJobIds, - cleanup: async () => { - await queueCleanup(); - await itemTypesCleanup(); - await userCleanup(); - await orgCleanup(); - await container.KyselyPg.destroy(); - await container.KyselyPgReadReplica.destroy(); - }, }; }); diff --git a/server/services/moderationConfigService/moderationConfigService.test.ts b/server/services/moderationConfigService/moderationConfigService.test.ts index b425296f..bcaa9527 100644 --- a/server/services/moderationConfigService/moderationConfigService.test.ts +++ b/server/services/moderationConfigService/moderationConfigService.test.ts @@ -8,6 +8,8 @@ import getBottle from '../../iocContainer/index.js'; import createOrg from '../../test/fixtureHelpers/createOrg.js'; import createRule from '../../test/fixtureHelpers/createRule.js'; import createUser from '../../test/fixtureHelpers/createUser.js'; +import { makeTransactionalTestWithFixture } from '../../test/harness/transactionalTest.js'; +import { type MockedServer } from '../../test/setupMockedServer.js'; import { makeMockPgDialect, type MockPgExecute, @@ -19,215 +21,145 @@ import { type ModerationConfigServicePg } from './dbTypes.js'; import { RuleStatus, RuleType, - type Action, type ConditionSet, - type ItemType, type Policy, - type UserItemType, } from './index.js'; import { ModerationConfigService } from './moderationConfigService.js'; import { PolicyType } from './types/policies.js'; -describe('ModerationConfigService', () => { - let container: Awaited>['container']; - let sutWithPrimary: ModerationConfigService; - let sutWithReadReplica: ModerationConfigService; - let defaultUserItemType: UserItemType; - - // NB: because we don't create a new org for each tests (that feels like - // overkill), we have to track entities added in each write test, by adding - // them to the variables below, so that we can assert on the results when - // reading. - let allCreatedItemTypes = [] as ItemType[]; - const createdItemTypes = { - get ALL() { - return allCreatedItemTypes; - }, - get USER() { - return allCreatedItemTypes.filter((it) => it.kind === 'USER'); - }, - get CONTENT() { - return allCreatedItemTypes.filter((it) => it.kind === 'CONTENT'); - }, - get THREAD() { - return allCreatedItemTypes.filter((it) => it.kind === 'THREAD'); - }, - }; - - let createdActions = [] as Action[]; - - const createdPolicies = [ - { - id: '1', - name: 'Example policy', - orgId: 'orgId', - parentId: 'parentId', - createdAt: new Date(), - updatedAt: new Date(), - semanticVersion: 1, - policyText: '', - policyType: PolicyType.DRUG_SALES, - userStrikeCount: 1, - applyUserStrikeCountConfigToChildren: false, - penalty: 'NONE', - }, - ] satisfies Policy[]; - - const dummyOrgId = uid(); - let dummyOrgCleanup: () => Promise; - const dummySchema = [ - { name: 'fakeField', type: 'STRING', required: false, container: null }, - ] as const; - - // Every time we'll run these tests, we'll generate a new org from scratch, - // and then delete it at the end (which should hopefully do a cascading delete - // of most/all of its relevant data). Testing this way let's us truly test the - // moderationConfigService as a black box -- inserting data only using the - // public methods, and then verifying that we can retrieve it or delete it - // with only the public methods. Any other approach would require our tests to - // memorize exactly what queries the service is issuing and the schema of the - // underlying db tables, which makes the tests more brittle/harder to maintain - // than I'd like if the service is refactored. - beforeAll(async () => { - container = (await getBottle()).container; - - // An instance of kysely that will throw if any queries are run through it; - // used to test that the moderationConfigService is querying the correct db. - const kyselyShouldBeUnused = new Kysely({ - dialect: makeMockPgDialect( - jest.fn().mockImplementation(async () => { - throw new Error('Did not expect this kysely instance to be used!'); - }), - ), - }); - - // In order to test that the correct db is queried (i.e., replicas vs the - // primary), we'll just use different instances of the service, where each - // only has access to the db we expect to be hit. +type TestDeps = MockedServer['deps']; + +type Sut = ConstructorParameters[0]; + +// We test the moderationConfigService as a black box: every test gets a fresh +// org and seeds data only through the public methods, then verifies it can read +// or delete that data with only the public methods. The transactional harness +// rolls everything back after each test, so there's no cleanup and no state +// leaking between tests (and therefore no ordering dependency between them). +// +// To test that the correct db is queried (i.e., replicas vs the primary), we +// use two service instances, each with access only to the db we expect to be +// hit; the other db is a kysely instance that throws if it's ever used. +function makeSuts(primary: Sut, replica: Sut) { + const kyselyShouldBeUnused = new Kysely({ + dialect: makeMockPgDialect( + jest.fn().mockImplementation(async () => { + throw new Error('Did not expect this kysely instance to be used!'); + }), + ), + }); - sutWithPrimary = new ModerationConfigService( - container.KyselyPg, + return { + sutWithPrimary: new ModerationConfigService( + primary, kyselyShouldBeUnused, async () => {}, - ); - - sutWithReadReplica = new ModerationConfigService( + ), + sutWithReadReplica: new ModerationConfigService( kyselyShouldBeUnused, - container.KyselyPgReadReplica, + replica, async () => {}, - ); + ), + }; +} - const createOrgResult = await createOrg( - { - KyselyPg: container.KyselyPg, - ModerationConfigService: container.ModerationConfigService, - ApiKeyService: container.ApiKeyService, - }, - dummyOrgId, - ); +async function setupOrg(deps: TestDeps) { + const suts = makeSuts(deps.KyselyPg, deps.KyselyPgReadReplica); + const { org, defaultUserItemType } = await createOrg( + { + KyselyPg: deps.KyselyPg, + ModerationConfigService: deps.ModerationConfigService, + ApiKeyService: deps.ApiKeyService, + }, + uid(), + ); + return { ...suts, org, defaultUserItemType }; +} + +type SupportsReplicaMethod = Satisfies< + | 'getItemTypes' + | 'getItemType' + | 'getItemTypesByKind' + | 'getDefaultUserType' + | 'getItemTypesForAction' + | 'getItemTypesForRule' + | 'getActions' + | 'getPolicies', + keyof ModerationConfigService +>; + +async function expectReadReplicaUse( + suts: { + sutWithPrimary: ModerationConfigService; + sutWithReadReplica: ModerationConfigService; + }, + method: T, + baseFilter: Parameters[0], +) { + // cast baseFilter to prevent TS errors that would arise because TS can't + // verify that the particular string that `method` takes on at runtime + // corresponsds to the particular binding for baseFilter. + const filters = baseFilter as UnionToIntersection< + Parameters[0] + >; - defaultUserItemType = createOrgResult.defaultUserItemType; - dummyOrgCleanup = createOrgResult.cleanup; - allCreatedItemTypes = [...allCreatedItemTypes, defaultUserItemType]; - }); + // We're calling these to test that none of them throw, which'll only be + // true if the proper db is used. + await suts.sutWithPrimary[method](filters); + await suts.sutWithPrimary[method]({ ...filters, readFromReplica: false }); + await suts.sutWithReadReplica[method]({ ...filters, readFromReplica: true }); +} - afterAll(async () => { - await dummyOrgCleanup(); +const dummySchema = [ + { name: 'fakeField', type: 'STRING', required: false, container: null }, +] as const; - await Promise.all([ - container.KyselyPg.destroy(), - container.KyselyPgReadReplica.destroy(), - ]); - }); +const minimalRuleConditionSet = { + conjunction: 'AND' as const, + conditions: [ + { + input: { type: 'FULL_ITEM' as const }, + comparator: 'IS_NOT_PROVIDED' as const, + }, + ], +} satisfies ConditionSet; - const itemTypeSnapshotMatchers = { - id: expect.any(String), - version: expect.any(String), - orgId: expect.any(String), - }; +const itemTypeSnapshotMatchers = { + id: expect.any(String), + version: expect.any(String), + orgId: expect.any(String), +}; - const actionSnapshotMatchers = { - id: expect.any(String), - orgId: expect.any(String), - }; +const actionSnapshotMatchers = { + id: expect.any(String), + orgId: expect.any(String), +}; - type SupportsReplicaMethod = Satisfies< - | 'getItemTypes' - | 'getItemType' - | 'getItemTypesByKind' - | 'getDefaultUserType' - | 'getItemTypesForAction' - | 'getItemTypesForRule' - | 'getActions' - | 'getPolicies', - keyof ModerationConfigService - >; - - async function testReadReplicaUse( - method: T, - baseFilter: Parameters[0], - ) { - // cast baseFilter to prevent TS errors that would arise because TS can't - // verify that the particular string that `method` takes on at runtime - // corresponsds to the particular binding for baseFilter. - const filters = baseFilter as UnionToIntersection< - Parameters[0] - >; - - // We're calling these to test that none of them throw, which'll only be - // true if the proper db is used. - await sutWithPrimary[method](filters); - await sutWithPrimary[method]({ ...filters, readFromReplica: false }); - await sutWithReadReplica[method]({ ...filters, readFromReplica: true }); - } - - const minimalRuleConditionSet = { - conjunction: 'AND' as const, - conditions: [ - { - input: { type: 'FULL_ITEM' as const }, - comparator: 'IS_NOT_PROVIDED' as const, - }, - ], - } satisfies ConditionSet; +describe('ModerationConfigService', () => { + const testWithOrg = makeTransactionalTestWithFixture(async ({ deps }) => + setupOrg(deps), + ); describe('#getRuleByIdAndOrg', () => { - const testWithRuleRow = makeTestWithFixture(async () => { - const { org, cleanup: orgCleanup } = await createOrg( - { - KyselyPg: container.KyselyPg, - ModerationConfigService: container.ModerationConfigService, - ApiKeyService: container.ApiKeyService, - }, - uid(), - ); - const { user, cleanup: userCleanup } = await createUser( - container.KyselyPg, - org.id, - ); - const rule = await createRule(container.KyselyPg, org.id, { - creator: user, - name: 'getRuleByIdAndOrg fixture rule', - ruleType: RuleType.USER, - status: RuleStatus.DRAFT, - conditionSet: minimalRuleConditionSet, - }); + const testWithRuleRow = makeTransactionalTestWithFixture( + async ({ deps }) => { + const base = await setupOrg(deps); + const { user } = await createUser(deps.KyselyPg, base.org.id); + const rule = await createRule(deps.KyselyPg, base.org.id, { + creator: user, + name: 'getRuleByIdAndOrg fixture rule', + ruleType: RuleType.USER, + status: RuleStatus.DRAFT, + conditionSet: minimalRuleConditionSet, + }); - return { - org, - user, - ruleId: rule.id, - async cleanup() { - await rule.destroy(); - await userCleanup(); - await orgCleanup(); - }, - }; - }); + return { ...base, user, ruleId: rule.id }; + }, + ); testWithRuleRow( 'returns the rule when the org id matches the rule row', - async ({ org, ruleId }) => { + async ({ sutWithPrimary, org, ruleId }) => { const row = await sutWithPrimary.getRuleByIdAndOrg(ruleId, org.id, { readFromReplica: false, }); @@ -239,273 +171,322 @@ describe('ModerationConfigService', () => { testWithRuleRow( 'returns null when the org id does not match (IDOR guard)', - async ({ ruleId }) => { - const { org: otherOrg, cleanup: otherOrgCleanup } = await createOrg( + async ({ sutWithPrimary, deps, ruleId }) => { + const { org: otherOrg } = await createOrg( { - KyselyPg: container.KyselyPg, - ModerationConfigService: container.ModerationConfigService, - ApiKeyService: container.ApiKeyService, + KyselyPg: deps.KyselyPg, + ModerationConfigService: deps.ModerationConfigService, + ApiKeyService: deps.ApiKeyService, }, uid(), ); - try { - const row = await sutWithPrimary.getRuleByIdAndOrg( - ruleId, - otherOrg.id, - { readFromReplica: false }, - ); - expect(row).toBeNull(); - } finally { - await otherOrgCleanup(); - } + const row = await sutWithPrimary.getRuleByIdAndOrg( + ruleId, + otherOrg.id, + { + readFromReplica: false, + }, + ); + expect(row).toBeNull(); }, ); }); - // NB: there is an ordering dependency between these tests, as the creation - // tests run first and then the read tests assert on the presence of their - // writes. describe('ItemType-Returning methods', () => { describe('Creation methods', () => { describe('#createContentType', () => { - it('should return and durably save the new item type', async () => { - const saved = await sutWithPrimary.createContentType(dummyOrgId, { - schema: dummySchema, - description: null, - name: 'Content Item Type', - schemaFieldRoles: { - displayName: 'fakeField', - }, - }); + testWithOrg( + 'should return and durably save the new item type', + async ({ sutWithPrimary, org }) => { + const saved = await sutWithPrimary.createContentType(org.id, { + schema: dummySchema, + description: null, + name: 'Content Item Type', + schemaFieldRoles: { + displayName: 'fakeField', + }, + }); - const fetched = await sutWithPrimary.getItemType({ - orgId: dummyOrgId, - itemTypeSelector: { id: saved.id }, - }); + const fetched = await sutWithPrimary.getItemType({ + orgId: org.id, + itemTypeSelector: { id: saved.id }, + }); - expect(saved).toMatchInlineSnapshot( - itemTypeSnapshotMatchers, - ` - { - "description": null, - "id": Any, - "kind": "CONTENT", - "name": "Content Item Type", - "orgId": Any, - "schema": [ - { - "container": null, - "name": "fakeField", - "required": false, - "type": "STRING", + expect(saved).toMatchInlineSnapshot( + itemTypeSnapshotMatchers, + ` + { + "description": null, + "id": Any, + "kind": "CONTENT", + "name": "Content Item Type", + "orgId": Any, + "schema": [ + { + "container": null, + "name": "fakeField", + "required": false, + "type": "STRING", + }, + ], + "schemaFieldRoles": { + "createdAt": undefined, + "creatorId": undefined, + "displayName": "fakeField", + "ipAddress": undefined, + "isDeleted": undefined, + "parentId": undefined, + "threadId": undefined, }, - ], - "schemaFieldRoles": { - "createdAt": undefined, - "creatorId": undefined, - "displayName": "fakeField", - "ipAddress": undefined, - "isDeleted": undefined, - "parentId": undefined, - "threadId": undefined, - }, - "schemaVariant": "original", - "version": Any, - } - `, - ); - expect(saved.orgId).toBe(dummyOrgId); - expect(saved).toEqual(fetched); - allCreatedItemTypes = [...allCreatedItemTypes, saved]; - }); + "schemaVariant": "original", + "version": Any, + } + `, + ); + expect(saved.orgId).toBe(org.id); + expect(saved).toEqual(fetched); + }, + ); }); describe('#createThreadType', () => { - it('should return and durably save the new item type', async () => { - const saved = await sutWithPrimary.createThreadType(dummyOrgId, { - schema: dummySchema, - description: 'Test description', - name: 'Thread Item Type', - schemaFieldRoles: { - displayName: 'fakeField', - }, - }); + testWithOrg( + 'should return and durably save the new item type', + async ({ sutWithPrimary, org }) => { + const saved = await sutWithPrimary.createThreadType(org.id, { + schema: dummySchema, + description: 'Test description', + name: 'Thread Item Type', + schemaFieldRoles: { + displayName: 'fakeField', + }, + }); - const fetched = await sutWithPrimary.getItemType({ - orgId: dummyOrgId, - itemTypeSelector: { id: saved.id }, - }); + const fetched = await sutWithPrimary.getItemType({ + orgId: org.id, + itemTypeSelector: { id: saved.id }, + }); - expect(saved).toMatchInlineSnapshot( - itemTypeSnapshotMatchers, - ` - { - "description": "Test description", - "id": Any, - "kind": "THREAD", - "name": "Thread Item Type", - "orgId": Any, - "schema": [ - { - "container": null, - "name": "fakeField", - "required": false, - "type": "STRING", + expect(saved).toMatchInlineSnapshot( + itemTypeSnapshotMatchers, + ` + { + "description": "Test description", + "id": Any, + "kind": "THREAD", + "name": "Thread Item Type", + "orgId": Any, + "schema": [ + { + "container": null, + "name": "fakeField", + "required": false, + "type": "STRING", + }, + ], + "schemaFieldRoles": { + "createdAt": undefined, + "creatorId": undefined, + "displayName": "fakeField", + "ipAddress": undefined, + "isDeleted": undefined, }, - ], - "schemaFieldRoles": { - "createdAt": undefined, - "creatorId": undefined, - "displayName": "fakeField", - "ipAddress": undefined, - "isDeleted": undefined, - }, - "schemaVariant": "original", - "version": Any, - } - `, - ); - expect(saved.orgId).toBe(dummyOrgId); - expect(saved).toEqual(fetched); - allCreatedItemTypes = [...allCreatedItemTypes, saved]; - }); + "schemaVariant": "original", + "version": Any, + } + `, + ); + expect(saved.orgId).toBe(org.id); + expect(saved).toEqual(fetched); + }, + ); }); describe('#createUserType', () => { - it('should return and durably save the new item type', async () => { - const saved = await sutWithPrimary.createUserType(dummyOrgId, { - schema: dummySchema, - description: null, - name: 'User Item Type', - schemaFieldRoles: { - displayName: 'fakeField', - }, - }); + testWithOrg( + 'should return and durably save the new item type', + async ({ sutWithPrimary, org }) => { + const saved = await sutWithPrimary.createUserType(org.id, { + schema: dummySchema, + description: null, + name: 'User Item Type', + schemaFieldRoles: { + displayName: 'fakeField', + }, + }); - const fetched = await sutWithPrimary.getItemType({ - orgId: dummyOrgId, - itemTypeSelector: { id: saved.id }, - }); + const fetched = await sutWithPrimary.getItemType({ + orgId: org.id, + itemTypeSelector: { id: saved.id }, + }); - expect(saved).toMatchInlineSnapshot( - itemTypeSnapshotMatchers, - ` - { - "description": null, - "id": Any, - "isDefaultUserType": false, - "kind": "USER", - "name": "User Item Type", - "orgId": Any, - "schema": [ - { - "container": null, - "name": "fakeField", - "required": false, - "type": "STRING", + expect(saved).toMatchInlineSnapshot( + itemTypeSnapshotMatchers, + ` + { + "description": null, + "id": Any, + "isDefaultUserType": false, + "kind": "USER", + "name": "User Item Type", + "orgId": Any, + "schema": [ + { + "container": null, + "name": "fakeField", + "required": false, + "type": "STRING", + }, + ], + "schemaFieldRoles": { + "backgroundImage": undefined, + "createdAt": undefined, + "displayName": "fakeField", + "email": undefined, + "ipAddress": undefined, + "isDeleted": undefined, + "profileIcon": undefined, }, - ], - "schemaFieldRoles": { - "backgroundImage": undefined, - "createdAt": undefined, - "displayName": "fakeField", - "email": undefined, - "ipAddress": undefined, - "isDeleted": undefined, - "profileIcon": undefined, - }, - "schemaVariant": "original", - "version": Any, - } - `, - ); - expect(saved.orgId).toBe(dummyOrgId); - expect(saved).toEqual(fetched); - allCreatedItemTypes = [...allCreatedItemTypes, saved]; - }); + "schemaVariant": "original", + "version": Any, + } + `, + ); + expect(saved.orgId).toBe(org.id); + expect(saved).toEqual(fetched); + }, + ); }); }); describe('Read methods', () => { describe('#getItemTypes', () => { - it('should return all item types, properly formatted', async () => { - const res = await sutWithPrimary.getItemTypes({ orgId: dummyOrgId }); - expect(res).toHaveLength(createdItemTypes.ALL.length); - expect(res).toEqual(expect.arrayContaining(createdItemTypes.ALL)); - }); + testWithOrg( + 'should return all item types, properly formatted', + async ({ sutWithPrimary, org, defaultUserItemType }) => { + const contentType = await sutWithPrimary.createContentType(org.id, { + schema: dummySchema, + description: null, + name: 'Content Item Type', + schemaFieldRoles: { displayName: 'fakeField' }, + }); + const threadType = await sutWithPrimary.createThreadType(org.id, { + schema: dummySchema, + description: null, + name: 'Thread Item Type', + schemaFieldRoles: { displayName: 'fakeField' }, + }); + const userType = await sutWithPrimary.createUserType(org.id, { + schema: dummySchema, + description: null, + name: 'User Item Type', + schemaFieldRoles: { displayName: 'fakeField' }, + }); + + const expected = [ + defaultUserItemType, + contentType, + threadType, + userType, + ]; + const res = await sutWithPrimary.getItemTypes({ orgId: org.id }); + expect(res).toHaveLength(expected.length); + expect(res).toEqual(expect.arrayContaining(expected)); + }, + ); }); describe('#getItemTypesByKind', () => { - it('should filter by kind', async () => { - const [userItemTypes, contentItemTypes, threadItemTypes] = - await Promise.all([ - sutWithPrimary.getItemTypesByKind({ - orgId: dummyOrgId, - kind: 'USER', - }), - sutWithPrimary.getItemTypesByKind({ - orgId: dummyOrgId, - kind: 'CONTENT', - }), - sutWithPrimary.getItemTypesByKind({ - orgId: dummyOrgId, - kind: 'THREAD', - }), - ]); + testWithOrg( + 'should filter by kind', + async ({ sutWithPrimary, org, defaultUserItemType }) => { + const contentType = await sutWithPrimary.createContentType(org.id, { + schema: dummySchema, + description: null, + name: 'Content Item Type', + schemaFieldRoles: { displayName: 'fakeField' }, + }); + const threadType = await sutWithPrimary.createThreadType(org.id, { + schema: dummySchema, + description: null, + name: 'Thread Item Type', + schemaFieldRoles: { displayName: 'fakeField' }, + }); + const userType = await sutWithPrimary.createUserType(org.id, { + schema: dummySchema, + description: null, + name: 'User Item Type', + schemaFieldRoles: { displayName: 'fakeField' }, + }); - expect(userItemTypes).toHaveLength(createdItemTypes.USER.length); - expect(userItemTypes).toEqual( - expect.arrayContaining(createdItemTypes.USER), - ); + const userItemTypes = await sutWithPrimary.getItemTypesByKind({ + orgId: org.id, + kind: 'USER', + }); + const contentItemTypes = await sutWithPrimary.getItemTypesByKind({ + orgId: org.id, + kind: 'CONTENT', + }); + const threadItemTypes = await sutWithPrimary.getItemTypesByKind({ + orgId: org.id, + kind: 'THREAD', + }); - expect(contentItemTypes).toHaveLength( - createdItemTypes.CONTENT.length, - ); - expect(contentItemTypes).toEqual( - expect.arrayContaining(createdItemTypes.CONTENT), - ); + expect(userItemTypes).toHaveLength(2); + expect(userItemTypes).toEqual( + expect.arrayContaining([defaultUserItemType, userType]), + ); - expect(threadItemTypes).toHaveLength(createdItemTypes.THREAD.length); - expect(threadItemTypes).toEqual( - expect.arrayContaining(createdItemTypes.THREAD), - ); - }); + expect(contentItemTypes).toEqual([contentType]); + expect(threadItemTypes).toEqual([threadType]); + }, + ); }); describe('#getDefaultUserType', () => { - it('should return the defualt user type, properly formatted', async () => { - const res = await sutWithPrimary.getDefaultUserType({ - orgId: dummyOrgId, - }); - expect(res).toEqual(defaultUserItemType); - }); + testWithOrg( + 'should return the default user type, properly formatted', + async ({ sutWithPrimary, org, defaultUserItemType }) => { + const res = await sutWithPrimary.getDefaultUserType({ + orgId: org.id, + }); + expect(res).toEqual(defaultUserItemType); + }, + ); }); describe('#getItemTypesForAction', () => { - it('should query from the proper db', async () => { - // These tests will throw if the wrong db is used (see kyselyShouldBeUnused) - await sutWithPrimary.getItemTypesForAction({ - orgId: dummyOrgId, - actionId: 'someId', - directives: { maxAge: 0 }, - }); - await sutWithReadReplica.getItemTypesForAction({ - orgId: dummyOrgId, - actionId: 'someId', - directives: { maxAge: 10 }, - }); - }); + testWithOrg( + 'should query from the proper db', + async ({ sutWithPrimary, sutWithReadReplica, org }) => { + // These tests will throw if the wrong db is used (see kyselyShouldBeUnused) + await sutWithPrimary.getItemTypesForAction({ + orgId: org.id, + actionId: 'someId', + directives: { maxAge: 0 }, + }); + await sutWithReadReplica.getItemTypesForAction({ + orgId: org.id, + actionId: 'someId', + directives: { maxAge: 10 }, + }); + }, + ); it.skip('should return the right results', () => {}); }); describe('#getItemTypesForRule', () => { - it('should query from the proper db', async () => { - await testReadReplicaUse('getItemTypesForRule', { - orgId: dummyOrgId, - ruleId: 'sasts', - }); - }); + testWithOrg( + 'should query from the proper db', + async ({ sutWithPrimary, sutWithReadReplica, org }) => { + await expectReadReplicaUse( + { sutWithPrimary, sutWithReadReplica }, + 'getItemTypesForRule', + { orgId: org.id, ruleId: 'sasts' }, + ); + }, + ); it.skip('should return the right results', () => {}); }); @@ -515,67 +496,63 @@ describe('ModerationConfigService', () => { describe('Action-returning methods', () => { describe('Creation methods', () => { describe('#upsertBuiltInActions', () => { - it('seeds the three built-in (non-CUSTOM_ACTION) rows for the org', async () => { - const all = await sutWithPrimary.getActions({ orgId: dummyOrgId }); - const builtIns = all.filter( - (it) => it.actionType !== 'CUSTOM_ACTION', - ); - const types = builtIns.map((it) => it.actionType).sort(); - expect(types).toEqual( - [ - 'ENQUEUE_AUTHOR_TO_MRT', - 'ENQUEUE_TO_MRT', - 'ENQUEUE_TO_NCMEC', - ].sort(), - ); - for (const action of builtIns) { - expect(action.orgId).toBe(dummyOrgId); - expect(action).not.toHaveProperty('callbackUrl'); - } - }); + testWithOrg( + 'seeds the three built-in (non-CUSTOM_ACTION) rows for the org', + async ({ sutWithPrimary, org }) => { + const all = await sutWithPrimary.getActions({ orgId: org.id }); + const builtIns = all.filter( + (it) => it.actionType !== 'CUSTOM_ACTION', + ); + const types = builtIns.map((it) => it.actionType).sort(); + expect(types).toEqual( + [ + 'ENQUEUE_AUTHOR_TO_MRT', + 'ENQUEUE_TO_MRT', + 'ENQUEUE_TO_NCMEC', + ].sort(), + ); + for (const action of builtIns) { + expect(action.orgId).toBe(org.id); + expect(action).not.toHaveProperty('callbackUrl'); + } + }, + ); - it('is idempotent: calling twice does not create duplicates', async () => { - const before = await sutWithPrimary.getActions({ - orgId: dummyOrgId, - }); - const beforeBuiltIns = before - .filter((it) => it.actionType !== 'CUSTOM_ACTION') - .map((it) => it.id) - .sort(); - await sutWithPrimary.upsertBuiltInActions(dummyOrgId); - const after = await sutWithPrimary.getActions({ - orgId: dummyOrgId, - }); - const afterBuiltIns = after - .filter((it) => it.actionType !== 'CUSTOM_ACTION') - .map((it) => it.id) - .sort(); - expect(afterBuiltIns).toEqual(beforeBuiltIns); - }); + testWithOrg( + 'is idempotent: calling twice does not create duplicates', + async ({ sutWithPrimary, org }) => { + const before = await sutWithPrimary.getActions({ + orgId: org.id, + }); + const beforeBuiltIns = before + .filter((it) => it.actionType !== 'CUSTOM_ACTION') + .map((it) => it.id) + .sort(); + await sutWithPrimary.upsertBuiltInActions(org.id); + const after = await sutWithPrimary.getActions({ + orgId: org.id, + }); + const afterBuiltIns = after + .filter((it) => it.actionType !== 'CUSTOM_ACTION') + .map((it) => it.id) + .sort(); + expect(afterBuiltIns).toEqual(beforeBuiltIns); + }, + ); - it('built-ins surface for the appropriate item type kinds', async () => { - const fresh = await createOrg( - { - KyselyPg: container.KyselyPg, - ModerationConfigService: container.ModerationConfigService, - ApiKeyService: container.ApiKeyService, - }, - uid(), - ); - try { - const contentType = await sutWithPrimary.createContentType( - fresh.org.id, - { - schema: dummySchema, - description: null, - name: faker.random.alphaNumeric(16), - schemaFieldRoles: { displayName: 'fakeField' }, - }, - ); + testWithOrg( + 'built-ins surface for the appropriate item type kinds', + async ({ sutWithPrimary, org, defaultUserItemType }) => { + const contentType = await sutWithPrimary.createContentType(org.id, { + schema: dummySchema, + description: null, + name: faker.random.alphaNumeric(16), + schemaFieldRoles: { displayName: 'fakeField' }, + }); const forUser = await sutWithPrimary.getActionsForItemType({ - orgId: fresh.org.id, - itemTypeId: fresh.defaultUserItemType.id, + orgId: org.id, + itemTypeId: defaultUserItemType.id, itemTypeKind: 'USER', }); expect(forUser.map((it) => it.actionType).sort()).toEqual( @@ -583,7 +560,7 @@ describe('ModerationConfigService', () => { ); const forContent = await sutWithPrimary.getActionsForItemType({ - orgId: fresh.org.id, + orgId: org.id, itemTypeId: contentType.id, itemTypeKind: 'CONTENT', }); @@ -594,95 +571,129 @@ describe('ModerationConfigService', () => { 'ENQUEUE_TO_NCMEC', ].sort(), ); - } finally { - await fresh.cleanup(); - } - }); + }, + ); }); describe('#createAction', () => { - it('should return and durably save the new action', async () => { - const saved = await sutWithPrimary.createAction(dummyOrgId, { - name: 'Test Action', - description: 'Test description', - type: 'CUSTOM_ACTION', - callbackUrl: 'https://example.com', - callbackUrlHeaders: null, - callbackUrlBody: null, - applyUserStrikes: false, - }); + testWithOrg( + 'should return and durably save the new action', + async ({ sutWithPrimary, org }) => { + const saved = await sutWithPrimary.createAction(org.id, { + name: 'Test Action', + description: 'Test description', + type: 'CUSTOM_ACTION', + callbackUrl: 'https://example.com', + callbackUrlHeaders: null, + callbackUrlBody: null, + applyUserStrikes: false, + }); - const [fetched] = await sutWithPrimary.getActions({ - orgId: dummyOrgId, - ids: [saved.id], - }); + const [fetched] = await sutWithPrimary.getActions({ + orgId: org.id, + ids: [saved.id], + }); - expect(saved).toMatchInlineSnapshot( - actionSnapshotMatchers, - ` - { - "actionType": "CUSTOM_ACTION", - "applyUserStrikes": false, - "callbackUrl": "https://example.com", - "callbackUrlBody": null, - "callbackUrlHeaders": null, - "customMrtApiParams": null, - "description": "Test description", - "id": Any, - "name": "Test Action", - "orgId": Any, - "penalty": "NONE", - } - `, - ); - expect(saved.orgId).toBe(dummyOrgId); - expect(saved).toEqual(fetched); - createdActions = [...createdActions, saved]; - }); + expect(saved).toMatchInlineSnapshot( + actionSnapshotMatchers, + ` + { + "actionType": "CUSTOM_ACTION", + "applyUserStrikes": false, + "callbackUrl": "https://example.com", + "callbackUrlBody": null, + "callbackUrlHeaders": null, + "customMrtApiParams": null, + "description": "Test description", + "id": Any, + "name": "Test Action", + "orgId": Any, + "penalty": "NONE", + } + `, + ); + expect(saved.orgId).toBe(org.id); + expect(saved).toEqual(fetched); + }, + ); }); }); describe('Read methods', () => { describe('#getActions', () => { - it('should query from the proper db', async () => { - await testReadReplicaUse('getActions', { orgId: dummyOrgId }); - }); + testWithOrg( + 'should query from the proper db', + async ({ sutWithPrimary, sutWithReadReplica, org }) => { + await expectReadReplicaUse( + { sutWithPrimary, sutWithReadReplica }, + 'getActions', + { orgId: org.id }, + ); + }, + ); - it('should return all actions, properly formatted', async () => { - const res = await sutWithPrimary.getActions({ orgId: dummyOrgId }); - const customActions = res.filter( - (it) => it.actionType === 'CUSTOM_ACTION', - ); - expect(customActions).toHaveLength(createdActions.length); - expect(customActions).toEqual(expect.arrayContaining(createdActions)); - }); + testWithOrg( + 'should return all custom actions, properly formatted', + async ({ sutWithPrimary, org }) => { + const createdActions = [ + await sutWithPrimary.createAction(org.id, { + name: faker.random.alphaNumeric(16), + description: 'Test description', + type: 'CUSTOM_ACTION', + callbackUrl: 'https://example.com', + callbackUrlHeaders: null, + callbackUrlBody: null, + applyUserStrikes: false, + }), + await sutWithPrimary.createAction(org.id, { + name: faker.random.alphaNumeric(16), + description: null, + type: 'CUSTOM_ACTION', + callbackUrl: 'https://example.com', + callbackUrlHeaders: null, + callbackUrlBody: null, + applyUserStrikes: false, + }), + ]; - it('should round-trip a non-null customMrtApiParams value', async () => { - const action = await sutWithPrimary.createAction(dummyOrgId, { - name: faker.random.alphaNumeric(16), - description: null, - type: 'CUSTOM_ACTION', - callbackUrl: 'https://example.com', - callbackUrlHeaders: null, - callbackUrlBody: null, - }); + const res = await sutWithPrimary.getActions({ orgId: org.id }); + const customActions = res.filter( + (it) => it.actionType === 'CUSTOM_ACTION', + ); + expect(customActions).toHaveLength(createdActions.length); + expect(customActions).toEqual( + expect.arrayContaining(createdActions), + ); + }, + ); - // Legacy shape pre-dating the typed parameter spec — set it via raw - // Kysely to verify the read mapping still surfaces older rows - // unchanged for back-compat. - const params = [ - { key: 'foo', value: 'bar' }, - { key: 'baz', value: 'qux' }, - ]; - await container.KyselyPg.updateTable('public.actions') - .set({ custom_mrt_api_params: params }) - .where('id', '=', action.id) - .where('org_id', '=', dummyOrgId) - .execute(); + testWithOrg( + 'should round-trip a non-null customMrtApiParams value', + async ({ sutWithPrimary, deps, org }) => { + const action = await sutWithPrimary.createAction(org.id, { + name: faker.random.alphaNumeric(16), + description: null, + type: 'CUSTOM_ACTION', + callbackUrl: 'https://example.com', + callbackUrlHeaders: null, + callbackUrlBody: null, + }); + + // Legacy shape pre-dating the typed parameter spec — set it via raw + // Kysely to verify the read mapping still surfaces older rows + // unchanged for back-compat. + const params = [ + { key: 'foo', value: 'bar' }, + { key: 'baz', value: 'qux' }, + ]; + await deps.KyselyPg.updateTable('public.actions') + .set({ custom_mrt_api_params: params }) + .where('id', '=', action.id) + .where('org_id', '=', org.id) + .execute(); - try { const [fetched] = await sutWithPrimary.getActions({ - orgId: dummyOrgId, + orgId: org.id, ids: [action.id], }); expect(fetched).toBeDefined(); @@ -691,121 +702,110 @@ describe('ModerationConfigService', () => { expect( (fetched as { customMrtApiParams: unknown }).customMrtApiParams, ).toEqual(params); - } finally { - await sutWithPrimary.deleteCustomAction({ - orgId: dummyOrgId, - actionId: action.id, - }); - } - }); + }, + ); - it('round-trips typed parameters through createAction', async () => { - const parameters = [ - { - name: 'num_days_banned', - displayName: 'Days to ban', - type: 'NUMBER', - required: true, - min: 1, - max: 365, - defaultValue: 7, - }, - { - name: 'reason', - displayName: 'Reason', - type: 'SELECT', - required: true, - options: [ - { value: 'spam', label: 'Spam' }, - { value: 'abuse', label: 'Abuse' }, - ], - }, - { - name: 'notify_user', - displayName: 'Notify user', - type: 'BOOLEAN', - required: false, - defaultValue: false, - }, - ]; + testWithOrg( + 'round-trips typed parameters through createAction', + async ({ sutWithPrimary, org }) => { + const parameters = [ + { + name: 'num_days_banned', + displayName: 'Days to ban', + type: 'NUMBER', + required: true, + min: 1, + max: 365, + defaultValue: 7, + }, + { + name: 'reason', + displayName: 'Reason', + type: 'SELECT', + required: true, + options: [ + { value: 'spam', label: 'Spam' }, + { value: 'abuse', label: 'Abuse' }, + ], + }, + { + name: 'notify_user', + displayName: 'Notify user', + type: 'BOOLEAN', + required: false, + defaultValue: false, + }, + ]; - const created = await sutWithPrimary.createAction(dummyOrgId, { - name: faker.random.alphaNumeric(16), - description: null, - type: 'CUSTOM_ACTION', - callbackUrl: 'https://example.com', - callbackUrlHeaders: null, - callbackUrlBody: null, - parameters, - }); + const created = await sutWithPrimary.createAction(org.id, { + name: faker.random.alphaNumeric(16), + description: null, + type: 'CUSTOM_ACTION', + callbackUrl: 'https://example.com', + callbackUrlHeaders: null, + callbackUrlBody: null, + parameters, + }); - try { const [fetched] = await sutWithPrimary.getActions({ - orgId: dummyOrgId, + orgId: org.id, ids: [created.id], }); expect(fetched.actionType).toBe('CUSTOM_ACTION'); const stored = (fetched as { customMrtApiParams: unknown }) .customMrtApiParams; expect(stored).toEqual(parameters); - } finally { - await sutWithPrimary.deleteCustomAction({ - orgId: dummyOrgId, - actionId: created.id, - }); - } - }); + }, + ); - it('rejects invalid parameters at create time', async () => { - await expect( - sutWithPrimary.createAction(dummyOrgId, { - name: faker.random.alphaNumeric(16), - description: null, - type: 'CUSTOM_ACTION', - callbackUrl: 'https://example.com', - callbackUrlHeaders: null, - callbackUrlBody: null, - parameters: [ - { - name: 'invalid name with spaces', - displayName: 'X', - type: 'STRING', - required: false, - }, - ], - }), - ).rejects.toMatchObject({ status: 400 }); - }); + testWithOrg( + 'rejects invalid parameters at create time', + async ({ sutWithPrimary, org }) => { + await expect( + sutWithPrimary.createAction(org.id, { + name: faker.random.alphaNumeric(16), + description: null, + type: 'CUSTOM_ACTION', + callbackUrl: 'https://example.com', + callbackUrlHeaders: null, + callbackUrlBody: null, + parameters: [ + { + name: 'invalid name with spaces', + displayName: 'X', + type: 'STRING', + required: false, + }, + ], + }), + ).rejects.toMatchObject({ status: 400 }); + }, + ); }); }); describe('Update methods', () => { describe('#updateCustomAction', () => { - const testWithAction = makeTestWithFixture(async () => { - const action = await sutWithPrimary.createAction(dummyOrgId, { - name: faker.random.alphaNumeric(16), - description: 'before', - type: 'CUSTOM_ACTION', - callbackUrl: 'https://before.example.com', - callbackUrlHeaders: null, - callbackUrlBody: null, - applyUserStrikes: false, - }); - return { - action, - async cleanup() { - await sutWithPrimary.deleteCustomAction({ - orgId: dummyOrgId, - actionId: action.id, - }); - }, - }; - }); + const testWithAction = makeTransactionalTestWithFixture( + async ({ deps }) => { + const base = await setupOrg(deps); + const action = await base.sutWithPrimary.createAction(base.org.id, { + name: faker.random.alphaNumeric(16), + description: 'before', + type: 'CUSTOM_ACTION', + callbackUrl: 'https://before.example.com', + callbackUrlHeaders: null, + callbackUrlBody: null, + applyUserStrikes: false, + }); + return { ...base, action }; + }, + ); testWithAction( 'should update user-editable fields and bump updated_at', - async ({ action }) => { - const before = await container.KyselyPg.selectFrom('public.actions') + async ({ sutWithPrimary, deps, org, action }) => { + const before = await deps.KyselyPg.selectFrom('public.actions') .select(['updated_at']) .where('id', '=', action.id) .executeTakeFirstOrThrow(); @@ -813,24 +813,21 @@ describe('ModerationConfigService', () => { // Wait briefly so updated_at can advance even on fast clocks. await new Promise((resolve) => setTimeout(resolve, 5)); - const updated = await sutWithPrimary.updateCustomAction( - dummyOrgId, - { - actionId: action.id, - patch: { - description: 'after', - callbackUrl: 'https://after.example.com', - applyUserStrikes: true, - }, + const updated = await sutWithPrimary.updateCustomAction(org.id, { + actionId: action.id, + patch: { + description: 'after', + callbackUrl: 'https://after.example.com', + applyUserStrikes: true, }, - ); + }); expect(updated.actionType).toBe('CUSTOM_ACTION'); expect(updated.description).toBe('after'); expect(updated.callbackUrl).toBe('https://after.example.com'); expect(updated.applyUserStrikes).toBe(true); - const after = await container.KyselyPg.selectFrom('public.actions') + const after = await deps.KyselyPg.selectFrom('public.actions') .select(['updated_at', 'description']) .where('id', '=', action.id) .executeTakeFirstOrThrow(); @@ -843,20 +840,20 @@ describe('ModerationConfigService', () => { testWithAction( 'should not bump updated_at for an empty patch with no itemTypeIds', - async ({ action }) => { - const before = await container.KyselyPg.selectFrom('public.actions') + async ({ sutWithPrimary, deps, org, action }) => { + const before = await deps.KyselyPg.selectFrom('public.actions') .select(['updated_at']) .where('id', '=', action.id) .executeTakeFirstOrThrow(); await new Promise((resolve) => setTimeout(resolve, 5)); - const result = await sutWithPrimary.updateCustomAction(dummyOrgId, { + const result = await sutWithPrimary.updateCustomAction(org.id, { actionId: action.id, patch: {}, }); - const after = await container.KyselyPg.selectFrom('public.actions') + const after = await deps.KyselyPg.selectFrom('public.actions') .select(['updated_at']) .where('id', '=', action.id) .executeTakeFirstOrThrow(); @@ -869,41 +866,37 @@ describe('ModerationConfigService', () => { testWithAction( 'should throw NotFound when called with the wrong org', - async ({ action }) => { - const otherOrg = await createOrg( + async ({ sutWithPrimary, deps, action }) => { + const { org: otherOrg } = await createOrg( { - KyselyPg: container.KyselyPg, - ModerationConfigService: container.ModerationConfigService, - ApiKeyService: container.ApiKeyService, + KyselyPg: deps.KyselyPg, + ModerationConfigService: deps.ModerationConfigService, + ApiKeyService: deps.ApiKeyService, }, uid(), ); - try { - await expect( - sutWithPrimary.updateCustomAction(otherOrg.org.id, { - actionId: action.id, - patch: { description: 'leaked' }, - }), - ).rejects.toThrow( - expect.objectContaining({ type: [ErrorType.NotFound] }), - ); - - // The action's row in the original org must be untouched. - const row = await container.KyselyPg.selectFrom('public.actions') - .select(['description']) - .where('id', '=', action.id) - .executeTakeFirstOrThrow(); - expect(row.description).toBe('before'); - } finally { - await otherOrg.cleanup(); - } + await expect( + sutWithPrimary.updateCustomAction(otherOrg.id, { + actionId: action.id, + patch: { description: 'leaked' }, + }), + ).rejects.toThrow( + expect.objectContaining({ type: [ErrorType.NotFound] }), + ); + + // The action's row in the original org must be untouched. + const row = await deps.KyselyPg.selectFrom('public.actions') + .select(['description']) + .where('id', '=', action.id) + .executeTakeFirstOrThrow(); + expect(row.description).toBe('before'); }, ); testWithAction( 'updates parameters when patch.parameters is supplied', - async ({ action }) => { - await sutWithPrimary.updateCustomAction(dummyOrgId, { + async ({ sutWithPrimary, org, action }) => { + await sutWithPrimary.updateCustomAction(org.id, { actionId: action.id, patch: { parameters: [ @@ -918,7 +911,7 @@ describe('ModerationConfigService', () => { }); const [afterSet] = await sutWithPrimary.getActions({ - orgId: dummyOrgId, + orgId: org.id, ids: [action.id], }); expect( @@ -933,12 +926,12 @@ describe('ModerationConfigService', () => { ]); // Passing `[]` should clear, not leave the existing list in place. - await sutWithPrimary.updateCustomAction(dummyOrgId, { + await sutWithPrimary.updateCustomAction(org.id, { actionId: action.id, patch: { parameters: [] }, }); const [afterClear] = await sutWithPrimary.getActions({ - orgId: dummyOrgId, + orgId: org.id, ids: [action.id], }); expect( @@ -950,8 +943,8 @@ describe('ModerationConfigService', () => { testWithAction( 'leaves parameters unchanged when patch.parameters is omitted', - async ({ action }) => { - await sutWithPrimary.updateCustomAction(dummyOrgId, { + async ({ sutWithPrimary, org, action }) => { + await sutWithPrimary.updateCustomAction(org.id, { actionId: action.id, patch: { parameters: [ @@ -964,12 +957,12 @@ describe('ModerationConfigService', () => { ], }, }); - await sutWithPrimary.updateCustomAction(dummyOrgId, { + await sutWithPrimary.updateCustomAction(org.id, { actionId: action.id, patch: { description: 'after' }, }); const [fetched] = await sutWithPrimary.getActions({ - orgId: dummyOrgId, + orgId: org.id, ids: [action.id], }); expect(fetched.description).toBe('after'); @@ -988,8 +981,8 @@ describe('ModerationConfigService', () => { testWithAction( 'should reject renaming onto an existing action name', - async ({ action }) => { - const other = await sutWithPrimary.createAction(dummyOrgId, { + async ({ sutWithPrimary, org, action }) => { + const other = await sutWithPrimary.createAction(org.id, { name: faker.random.alphaNumeric(16), description: null, type: 'CUSTOM_ACTION', @@ -997,100 +990,70 @@ describe('ModerationConfigService', () => { callbackUrlHeaders: null, callbackUrlBody: null, }); - try { - await expect( - sutWithPrimary.updateCustomAction(dummyOrgId, { - actionId: action.id, - patch: { name: other.name }, - }), - ).rejects.toThrow( - expect.objectContaining({ - type: [ErrorType.UniqueViolation], - }), - ); - } finally { - await sutWithPrimary.deleteCustomAction({ - orgId: dummyOrgId, - actionId: other.id, - }); - } + await expect( + sutWithPrimary.updateCustomAction(org.id, { + actionId: action.id, + patch: { name: other.name }, + }), + ).rejects.toThrow( + expect.objectContaining({ + type: [ErrorType.UniqueViolation], + }), + ); }, ); testWithAction( 'should replace the item-type junction when itemTypeIds is provided', - async ({ action }) => { - const itemTypeA = await sutWithPrimary.createContentType( - dummyOrgId, - { - schema: dummySchema, - description: null, - name: faker.random.alphaNumeric(16), - schemaFieldRoles: { displayName: 'fakeField' }, - }, - ); - const itemTypeB = await sutWithPrimary.createContentType( - dummyOrgId, - { - schema: dummySchema, - description: null, - name: faker.random.alphaNumeric(16), - schemaFieldRoles: { displayName: 'fakeField' }, - }, - ); + async ({ sutWithPrimary, deps, org, action }) => { + const itemTypeA = await sutWithPrimary.createContentType(org.id, { + schema: dummySchema, + description: null, + name: faker.random.alphaNumeric(16), + schemaFieldRoles: { displayName: 'fakeField' }, + }); + const itemTypeB = await sutWithPrimary.createContentType(org.id, { + schema: dummySchema, + description: null, + name: faker.random.alphaNumeric(16), + schemaFieldRoles: { displayName: 'fakeField' }, + }); - try { - await sutWithPrimary.updateCustomAction(dummyOrgId, { - actionId: action.id, - patch: {}, - itemTypeIds: [itemTypeA.id], - }); - expect( - await container.KyselyPg.selectFrom( - 'public.actions_and_item_types', - ) - .select(['item_type_id']) - .where('action_id', '=', action.id) - .execute(), - ).toEqual([{ item_type_id: itemTypeA.id }]); - - await sutWithPrimary.updateCustomAction(dummyOrgId, { - actionId: action.id, - patch: {}, - itemTypeIds: [itemTypeB.id], - }); - expect( - await container.KyselyPg.selectFrom( - 'public.actions_and_item_types', - ) - .select(['item_type_id']) - .where('action_id', '=', action.id) - .execute(), - ).toEqual([{ item_type_id: itemTypeB.id }]); - - await sutWithPrimary.updateCustomAction(dummyOrgId, { - actionId: action.id, - patch: {}, - itemTypeIds: [], - }); - expect( - await container.KyselyPg.selectFrom( - 'public.actions_and_item_types', - ) - .select(['item_type_id']) - .where('action_id', '=', action.id) - .execute(), - ).toEqual([]); - } finally { - await sutWithPrimary.deleteItemType({ - orgId: dummyOrgId, - itemTypeId: itemTypeA.id, - }); - await sutWithPrimary.deleteItemType({ - orgId: dummyOrgId, - itemTypeId: itemTypeB.id, - }); - } + await sutWithPrimary.updateCustomAction(org.id, { + actionId: action.id, + patch: {}, + itemTypeIds: [itemTypeA.id], + }); + expect( + await deps.KyselyPg.selectFrom('public.actions_and_item_types') + .select(['item_type_id']) + .where('action_id', '=', action.id) + .execute(), + ).toEqual([{ item_type_id: itemTypeA.id }]); + + await sutWithPrimary.updateCustomAction(org.id, { + actionId: action.id, + patch: {}, + itemTypeIds: [itemTypeB.id], + }); + expect( + await deps.KyselyPg.selectFrom('public.actions_and_item_types') + .select(['item_type_id']) + .where('action_id', '=', action.id) + .execute(), + ).toEqual([{ item_type_id: itemTypeB.id }]); + + await sutWithPrimary.updateCustomAction(org.id, { + actionId: action.id, + patch: {}, + itemTypeIds: [], + }); + expect( + await deps.KyselyPg.selectFrom('public.actions_and_item_types') + .select(['item_type_id']) + .where('action_id', '=', action.id) + .execute(), + ).toEqual([]); }, ); }); @@ -1098,148 +1061,127 @@ describe('ModerationConfigService', () => { describe('Delete methods', () => { describe('#deleteCustomAction', () => { - const testWithAction = makeTestWithFixture(async () => { - const action = await sutWithPrimary.createAction(dummyOrgId, { - name: faker.random.alphaNumeric(16), - description: null, - type: 'CUSTOM_ACTION', - callbackUrl: 'https://example.com', - callbackUrlHeaders: null, - callbackUrlBody: null, - }); - return { - action, - // Best-effort cleanup; the test under assertion may have already - // removed the row. - async cleanup() { - await sutWithPrimary - .deleteCustomAction({ - orgId: dummyOrgId, - actionId: action.id, - }) - .catch(() => {}); - }, - }; - }); + const testWithAction = makeTransactionalTestWithFixture( + async ({ deps }) => { + const base = await setupOrg(deps); + const action = await base.sutWithPrimary.createAction(base.org.id, { + name: faker.random.alphaNumeric(16), + description: null, + type: 'CUSTOM_ACTION', + callbackUrl: 'https://example.com', + callbackUrlHeaders: null, + callbackUrlBody: null, + }); + return { ...base, action }; + }, + ); testWithAction( 'should return true and delete the action on success', - async ({ action }) => { + async ({ sutWithPrimary, org, action }) => { const result = await sutWithPrimary.deleteCustomAction({ - orgId: dummyOrgId, + orgId: org.id, actionId: action.id, }); expect(result).toBe(true); expect( await sutWithPrimary.getActions({ - orgId: dummyOrgId, + orgId: org.id, ids: [action.id], }), ).toEqual([]); }, ); - it('should return false when the action does not exist', async () => { - const result = await sutWithPrimary.deleteCustomAction({ - orgId: dummyOrgId, - actionId: uid(), - }); - expect(result).toBe(false); - }); + testWithOrg( + 'should return false when the action does not exist', + async ({ sutWithPrimary, org }) => { + const result = await sutWithPrimary.deleteCustomAction({ + orgId: org.id, + actionId: uid(), + }); + expect(result).toBe(false); + }, + ); testWithAction( 'should return false when called with the wrong org and leave the row intact', - async ({ action }) => { - const otherOrg = await createOrg( + async ({ sutWithPrimary, deps, org, action }) => { + const { org: otherOrg } = await createOrg( { - KyselyPg: container.KyselyPg, - ModerationConfigService: container.ModerationConfigService, - ApiKeyService: container.ApiKeyService, + KyselyPg: deps.KyselyPg, + ModerationConfigService: deps.ModerationConfigService, + ApiKeyService: deps.ApiKeyService, }, uid(), ); - try { - const result = await sutWithPrimary.deleteCustomAction({ - orgId: otherOrg.org.id, - actionId: action.id, - }); - expect(result).toBe(false); - const [stillThere] = await sutWithPrimary.getActions({ - orgId: dummyOrgId, - ids: [action.id], - }); - expect(stillThere.id).toBe(action.id); - } finally { - await otherOrg.cleanup(); - } + const result = await sutWithPrimary.deleteCustomAction({ + orgId: otherOrg.id, + actionId: action.id, + }); + expect(result).toBe(false); + const [stillThere] = await sutWithPrimary.getActions({ + orgId: org.id, + ids: [action.id], + }); + expect(stillThere.id).toBe(action.id); }, ); testWithAction( 'should clean up rules_and_actions and actions_and_item_types junction rows', - async ({ action }) => { - const itemType = await sutWithPrimary.createContentType( - dummyOrgId, - { - schema: dummySchema, - description: null, - name: faker.random.alphaNumeric(16), - schemaFieldRoles: { displayName: 'fakeField' }, - }, - ); - const rule = await createRule(container.KyselyPg, dummyOrgId); + async ({ sutWithPrimary, deps, org, action }) => { + const itemType = await sutWithPrimary.createContentType(org.id, { + schema: dummySchema, + description: null, + name: faker.random.alphaNumeric(16), + schemaFieldRoles: { displayName: 'fakeField' }, + }); + const rule = await createRule(deps.KyselyPg, org.id); - await container.KyselyPg.insertInto('public.actions_and_item_types') + await deps.KyselyPg.insertInto('public.actions_and_item_types') .values({ action_id: action.id, item_type_id: itemType.id }) .execute(); - await container.KyselyPg.insertInto('public.rules_and_actions') + await deps.KyselyPg.insertInto('public.rules_and_actions') .values({ action_id: action.id, rule_id: rule.id }) .execute(); - try { - const result = await sutWithPrimary.deleteCustomAction({ - orgId: dummyOrgId, - actionId: action.id, - }); - expect(result).toBe(true); - expect( - await container.KyselyPg.selectFrom( - 'public.actions_and_item_types', - ) - .select(['action_id']) - .where('action_id', '=', action.id) - .execute(), - ).toEqual([]); - expect( - await container.KyselyPg.selectFrom('public.rules_and_actions') - .select(['action_id']) - .where('action_id', '=', action.id) - .execute(), - ).toEqual([]); - } finally { - await rule.destroy(); - await sutWithPrimary.deleteItemType({ - orgId: dummyOrgId, - itemTypeId: itemType.id, - }); - } + const result = await sutWithPrimary.deleteCustomAction({ + orgId: org.id, + actionId: action.id, + }); + expect(result).toBe(true); + expect( + await deps.KyselyPg.selectFrom('public.actions_and_item_types') + .select(['action_id']) + .where('action_id', '=', action.id) + .execute(), + ).toEqual([]); + expect( + await deps.KyselyPg.selectFrom('public.rules_and_actions') + .select(['action_id']) + .where('action_id', '=', action.id) + .execute(), + ).toEqual([]); }, ); }); }); describe('#getActionsForItemType', () => { - const testWithItemTypeAndActions = makeTestWithFixture(async () => { - const itemType = await sutWithPrimary.createContentType(dummyOrgId, { - schema: dummySchema, - description: null, - name: faker.random.alphaNumeric(16), - schemaFieldRoles: { displayName: 'fakeField' }, - }); + const testWithItemTypeAndActions = makeTransactionalTestWithFixture( + async ({ deps }) => { + const base = await setupOrg(deps); + const { sutWithPrimary, org } = base; - const viaJunctionAction = await sutWithPrimary.createAction( - dummyOrgId, - { + const itemType = await sutWithPrimary.createContentType(org.id, { + schema: dummySchema, + description: null, + name: faker.random.alphaNumeric(16), + schemaFieldRoles: { displayName: 'fakeField' }, + }); + + const viaJunctionAction = await sutWithPrimary.createAction(org.id, { name: faker.random.alphaNumeric(16), description: null, type: 'CUSTOM_ACTION', @@ -1247,76 +1189,62 @@ describe('ModerationConfigService', () => { callbackUrlHeaders: null, callbackUrlBody: null, itemTypeIds: [itemType.id], - }, - ); + }); - const viaAppliesAllAction = await sutWithPrimary.createAction( - dummyOrgId, - { + const viaAppliesAllAction = await sutWithPrimary.createAction( + org.id, + { + name: faker.random.alphaNumeric(16), + description: null, + type: 'CUSTOM_ACTION', + callbackUrl: 'https://example.com', + callbackUrlHeaders: null, + callbackUrlBody: null, + }, + ); + await deps.KyselyPg.updateTable('public.actions') + .set({ applies_to_all_items_of_kind: ['CONTENT'] }) + .where('id', '=', viaAppliesAllAction.id) + .execute(); + + // Action satisfying both branches; result should still include it once. + const viaBothAction = await sutWithPrimary.createAction(org.id, { name: faker.random.alphaNumeric(16), description: null, type: 'CUSTOM_ACTION', callbackUrl: 'https://example.com', callbackUrlHeaders: null, callbackUrlBody: null, - }, - ); - await container.KyselyPg.updateTable('public.actions') - .set({ applies_to_all_items_of_kind: ['CONTENT'] }) - .where('id', '=', viaAppliesAllAction.id) - .execute(); - - // Action satisfying both branches; result should still include it once. - const viaBothAction = await sutWithPrimary.createAction(dummyOrgId, { - name: faker.random.alphaNumeric(16), - description: null, - type: 'CUSTOM_ACTION', - callbackUrl: 'https://example.com', - callbackUrlHeaders: null, - callbackUrlBody: null, - itemTypeIds: [itemType.id], - }); - await container.KyselyPg.updateTable('public.actions') - .set({ applies_to_all_items_of_kind: ['CONTENT'] }) - .where('id', '=', viaBothAction.id) - .execute(); + itemTypeIds: [itemType.id], + }); + await deps.KyselyPg.updateTable('public.actions') + .set({ applies_to_all_items_of_kind: ['CONTENT'] }) + .where('id', '=', viaBothAction.id) + .execute(); - return { - itemType, - viaJunctionAction, - viaAppliesAllAction, - viaBothAction, - async cleanup() { - await Promise.all( - [ - viaJunctionAction.id, - viaAppliesAllAction.id, - viaBothAction.id, - ].map(async (id) => - sutWithPrimary.deleteCustomAction({ - orgId: dummyOrgId, - actionId: id, - }), - ), - ); - await sutWithPrimary.deleteItemType({ - orgId: dummyOrgId, - itemTypeId: itemType.id, - }); - }, - }; - }); + return { + ...base, + itemType, + viaJunctionAction, + viaAppliesAllAction, + viaBothAction, + }; + }, + ); testWithItemTypeAndActions( 'should return actions from both branches, deduped, scoped to the org', async ({ + sutWithPrimary, + deps, + org, itemType, viaJunctionAction, viaAppliesAllAction, viaBothAction, }) => { const result = await sutWithPrimary.getActionsForItemType({ - orgId: dummyOrgId, + orgId: org.id, itemTypeId: itemType.id, itemTypeKind: 'CONTENT', readFromReplica: false, @@ -1337,63 +1265,54 @@ describe('ModerationConfigService', () => { // Calling with a different org should never surface this org's // applies-to-all rows (they'd otherwise leak across orgs since the // ANY(...) predicate alone has no tenant scope). - const otherOrg = await createOrg( + const { org: otherOrg } = await createOrg( { - KyselyPg: container.KyselyPg, - ModerationConfigService: container.ModerationConfigService, - ApiKeyService: container.ApiKeyService, + KyselyPg: deps.KyselyPg, + ModerationConfigService: deps.ModerationConfigService, + ApiKeyService: deps.ApiKeyService, }, uid(), ); - try { - const otherResult = await sutWithPrimary.getActionsForItemType({ - orgId: otherOrg.org.id, - itemTypeId: itemType.id, - itemTypeKind: 'CONTENT', - readFromReplica: false, - }); - expect( - otherResult.filter((it) => it.actionType === 'CUSTOM_ACTION'), - ).toEqual([]); - } finally { - await otherOrg.cleanup(); - } + const otherResult = await sutWithPrimary.getActionsForItemType({ + orgId: otherOrg.id, + itemTypeId: itemType.id, + itemTypeKind: 'CONTENT', + readFromReplica: false, + }); + expect( + otherResult.filter((it) => it.actionType === 'CUSTOM_ACTION'), + ).toEqual([]); }, ); }); describe('#getActionsForRuleId', () => { - const testWithRuleAndAction = makeTestWithFixture(async () => { - const rule = await createRule(container.KyselyPg, dummyOrgId); - const action = await sutWithPrimary.createAction(dummyOrgId, { - name: faker.random.alphaNumeric(16), - description: null, - type: 'CUSTOM_ACTION', - callbackUrl: 'https://example.com', - callbackUrlHeaders: null, - callbackUrlBody: null, - }); - await container.KyselyPg.insertInto('public.rules_and_actions') - .values({ action_id: action.id, rule_id: rule.id }) - .execute(); - return { - rule, - action, - async cleanup() { - await sutWithPrimary.deleteCustomAction({ - orgId: dummyOrgId, - actionId: action.id, - }); - await rule.destroy(); - }, - }; - }); + const testWithRuleAndAction = makeTransactionalTestWithFixture( + async ({ deps }) => { + const base = await setupOrg(deps); + const { sutWithPrimary, org } = base; + + const rule = await createRule(deps.KyselyPg, org.id); + const action = await sutWithPrimary.createAction(org.id, { + name: faker.random.alphaNumeric(16), + description: null, + type: 'CUSTOM_ACTION', + callbackUrl: 'https://example.com', + callbackUrlHeaders: null, + callbackUrlBody: null, + }); + await deps.KyselyPg.insertInto('public.rules_and_actions') + .values({ action_id: action.id, rule_id: rule.id }) + .execute(); + return { ...base, rule, action }; + }, + ); testWithRuleAndAction( 'should return actions for a rule scoped to the caller org', - async ({ rule, action }) => { + async ({ sutWithPrimary, org, rule, action }) => { const result = await sutWithPrimary.getActionsForRuleId({ - orgId: dummyOrgId, + orgId: org.id, ruleId: rule.id, readFromReplica: false, }); @@ -1403,25 +1322,21 @@ describe('ModerationConfigService', () => { testWithRuleAndAction( 'should not return actions when called with a different org', - async ({ rule }) => { - const otherOrg = await createOrg( + async ({ sutWithPrimary, deps, rule }) => { + const { org: otherOrg } = await createOrg( { - KyselyPg: container.KyselyPg, - ModerationConfigService: container.ModerationConfigService, - ApiKeyService: container.ApiKeyService, + KyselyPg: deps.KyselyPg, + ModerationConfigService: deps.ModerationConfigService, + ApiKeyService: deps.ApiKeyService, }, uid(), ); - try { - const result = await sutWithPrimary.getActionsForRuleId({ - orgId: otherOrg.org.id, - ruleId: rule.id, - readFromReplica: false, - }); - expect(result).toEqual([]); - } finally { - await otherOrg.cleanup(); - } + const result = await sutWithPrimary.getActionsForRuleId({ + orgId: otherOrg.id, + ruleId: rule.id, + readFromReplica: false, + }); + expect(result).toEqual([]); }, ); }); @@ -1429,46 +1344,40 @@ describe('ModerationConfigService', () => { describe('Policy returning methods', () => { describe('Read methods', () => { - it('should query from the proper db', async () => { - await testReadReplicaUse('getPolicies', { orgId: dummyOrgId }); - }); + testWithOrg( + 'should query from the proper db', + async ({ sutWithPrimary, sutWithReadReplica, org }) => { + await expectReadReplicaUse( + { sutWithPrimary, sutWithReadReplica }, + 'getPolicies', + { orgId: org.id }, + ); + }, + ); // TODO: Fill in this test once we've implemented the policy mutations - it.skip('should return all policies, properly formatted', async () => { - const res = await sutWithPrimary.getPolicies({ orgId: dummyOrgId }); - expect(res).toHaveLength(createdPolicies.length); - expect(res).toEqual(expect.arrayContaining(createdPolicies)); - }); + testWithOrg.skip( + 'should return all policies, properly formatted', + async ({ sutWithPrimary, org }) => { + const createdPolicies = [] as Policy[]; + const res = await sutWithPrimary.getPolicies({ orgId: org.id }); + expect(res).toHaveLength(createdPolicies.length); + expect(res).toEqual(expect.arrayContaining(createdPolicies)); + }, + ); }); describe('Mutations', () => { - const testWithUserAndOrg = makeTestWithFixture(async () => { - const { org, cleanup: orgCleanup } = await createOrg( - { - KyselyPg: container.KyselyPg, - ModerationConfigService: container.ModerationConfigService, - ApiKeyService: container.ApiKeyService, - }, - uid(), - ); - - const { user, cleanup: userCleanup } = await createUser( - container.KyselyPg, - org.id, - ); - - return { - org, - user, - async cleanup() { - await userCleanup(); - await orgCleanup(); - }, - }; - }); + const testWithUserAndOrg = makeTransactionalTestWithFixture( + async ({ deps }) => { + const base = await setupOrg(deps); + const { user } = await createUser(deps.KyselyPg, base.org.id); + return { ...base, user }; + }, + ); testWithUserAndOrg( 'should create a root policy', - async ({ org, user }) => { + async ({ sutWithPrimary, org, user }) => { const policy = await sutWithPrimary.createPolicy({ orgId: org.id, policy: { @@ -1493,7 +1402,7 @@ describe('ModerationConfigService', () => { testWithUserAndOrg( 'should create parent and child policies', - async ({ org, user }) => { + async ({ sutWithPrimary, org, user }) => { const parentPolicy = await sutWithPrimary.createPolicy({ orgId: org.id, policy: { @@ -1536,7 +1445,7 @@ describe('ModerationConfigService', () => { testWithUserAndOrg( 'should update an existing policy', - async ({ org, user }) => { + async ({ sutWithPrimary, org, user }) => { const policy = await sutWithPrimary.createPolicy({ orgId: org.id, policy: { @@ -1580,7 +1489,7 @@ describe('ModerationConfigService', () => { testWithUserAndOrg( 'Prevent creation of policy with the same name as an existing policy', - async ({ org, user }) => { + async ({ sutWithPrimary, org, user }) => { await sutWithPrimary.createPolicy({ orgId: org.id, policy: { @@ -1621,135 +1530,153 @@ describe('ModerationConfigService', () => { }); }); describe('TextBank-returning methods', () => { - let createdTextBanks = [] as { - id: string; - orgId: string; - name: string; - description: string | null; - type: 'STRING' | 'REGEX'; - createdAt: Date; - updatedAt: Date; - ownerId: string | null; - strings: string[]; - }[]; - describe('Mutations', () => { describe('#createTextBank', () => { - it('should create a text bank', async () => { - const textBank = await sutWithPrimary.createTextBank(dummyOrgId, { - name: 'Test Text Bank', - description: 'Test description', - type: 'STRING' as const, - strings: ['test entry 1', 'test entry 2'], - }); - - expect(textBank).toEqual( - expect.objectContaining({ - createdAt: expect.any(Date), - updatedAt: expect.any(Date), - description: 'Test description', - id: expect.any(String), + testWithOrg( + 'should create a text bank', + async ({ sutWithPrimary, org }) => { + const textBank = await sutWithPrimary.createTextBank(org.id, { name: 'Test Text Bank', - orgId: expect.any(String), - ownerId: null, + description: 'Test description', + type: 'STRING' as const, strings: ['test entry 1', 'test entry 2'], - type: 'STRING', - }), - ); + }); - expect(textBank.orgId).toBe(dummyOrgId); - createdTextBanks = [...createdTextBanks, textBank]; - }); + expect(textBank).toEqual( + expect.objectContaining({ + createdAt: expect.any(Date), + updatedAt: expect.any(Date), + description: 'Test description', + id: expect.any(String), + name: 'Test Text Bank', + orgId: expect.any(String), + ownerId: null, + strings: ['test entry 1', 'test entry 2'], + type: 'STRING', + }), + ); + + expect(textBank.orgId).toBe(org.id); + }, + ); }); }); describe('Read methods', () => { describe('#getTextBanks', () => { - it('should return all text banks, properly formatted', async () => { - const res = await sutWithPrimary.getTextBanks({ orgId: dummyOrgId }); - expect(res).toHaveLength(createdTextBanks.length); - expect(res).toEqual(expect.arrayContaining(createdTextBanks)); - }); + testWithOrg( + 'should return all text banks, properly formatted', + async ({ sutWithPrimary, org }) => { + const createdTextBanks = [ + await sutWithPrimary.createTextBank(org.id, { + name: 'Test Text Bank 1', + description: 'Test description', + type: 'STRING' as const, + strings: ['test entry 1', 'test entry 2'], + }), + await sutWithPrimary.createTextBank(org.id, { + name: 'Test Text Bank 2', + description: null, + type: 'REGEX' as const, + strings: ['.*'], + }), + ]; + + const res = await sutWithPrimary.getTextBanks({ orgId: org.id }); + expect(res).toHaveLength(createdTextBanks.length); + expect(res).toEqual(expect.arrayContaining(createdTextBanks)); + }, + ); }); describe('#getTextBank', () => { - it('should return a specific text bank, properly formatted', async () => { - const textBank = createdTextBanks[0]; - const res = await sutWithPrimary.getTextBank({ - orgId: dummyOrgId, - id: textBank.id, - }); - expect(res).toEqual(textBank); - }); + testWithOrg( + 'should return a specific text bank, properly formatted', + async ({ sutWithPrimary, org }) => { + const textBank = await sutWithPrimary.createTextBank(org.id, { + name: 'Test Text Bank', + description: 'Test description', + type: 'STRING' as const, + strings: ['test entry 1', 'test entry 2'], + }); + + const res = await sutWithPrimary.getTextBank({ + orgId: org.id, + id: textBank.id, + }); + expect(res).toEqual(textBank); + }, + ); }); }); }); describe('#getItemType', () => { - const testWithOneItemTypeFixture = makeTestWithFixture(async () => { - const itemType = await sutWithPrimary.createContentType(dummyOrgId, { - schema: dummySchema, - description: null, - name: faker.random.alphaNumeric(16), - schemaFieldRoles: { - displayName: 'fakeField', - }, - }); + const testWithOneItemTypeFixture = makeTransactionalTestWithFixture( + async ({ deps }) => { + const base = await setupOrg(deps); + const itemType = await base.sutWithPrimary.createContentType( + base.org.id, + { + schema: dummySchema, + description: null, + name: faker.random.alphaNumeric(16), + schemaFieldRoles: { + displayName: 'fakeField', + }, + }, + ); - return { - itemType, - async cleanup() { - await sutWithPrimary.deleteItemType({ - orgId: dummyOrgId, - itemTypeId: itemType.id, - }); - }, - }; - }); + return { ...base, itemType }; + }, + ); - const testWithTwoItemTypesFixture = makeTestWithFixture(async () => { - const itemType = await sutWithPrimary.createContentType(dummyOrgId, { - schema: dummySchema, - description: null, - name: faker.random.alphaNumeric(16), - schemaFieldRoles: { - displayName: 'fakeField', - }, - }); + const testWithTwoItemTypesFixture = makeTransactionalTestWithFixture( + async ({ deps }) => { + const base = await setupOrg(deps); + const itemType = await base.sutWithPrimary.createContentType( + base.org.id, + { + schema: dummySchema, + description: null, + name: faker.random.alphaNumeric(16), + schemaFieldRoles: { + displayName: 'fakeField', + }, + }, + ); - const newItemType = await sutWithPrimary.updateContentType(dummyOrgId, { - id: itemType.id, - name: faker.random.alphaNumeric(16), - schemaFieldRoles: { - creatorId: undefined, - }, - }); + const newItemType = await base.sutWithPrimary.updateContentType( + base.org.id, + { + id: itemType.id, + name: faker.random.alphaNumeric(16), + schemaFieldRoles: { + creatorId: undefined, + }, + }, + ); - return { - itemType, - newItemType, - async cleanup() { - await sutWithPrimary.deleteItemType({ - orgId: dummyOrgId, - itemTypeId: itemType.id, - }); - }, - }; - }); + return { ...base, itemType, newItemType }; + }, + ); - it("Should return undefined if an item type with the given ID doesn't exist", async () => { - const itemType = await sutWithPrimary.getItemType({ - orgId: dummyOrgId, - itemTypeSelector: { id: 'fakeId' }, - }); + testWithOrg( + "Should return undefined if an item type with the given ID doesn't exist", + async ({ sutWithPrimary, org }) => { + const itemType = await sutWithPrimary.getItemType({ + orgId: org.id, + itemTypeSelector: { id: 'fakeId' }, + }); - expect(itemType).toBeUndefined(); - }); + expect(itemType).toBeUndefined(); + }, + ); testWithOneItemTypeFixture( 'Should return a partial item type if requested for a selector without a version', - async ({ itemType }) => { + async ({ sutWithPrimary, org, itemType }) => { const fetched = await sutWithPrimary.getItemType({ - orgId: dummyOrgId, + orgId: org.id, itemTypeSelector: { id: itemType.id, schemaVariant: 'partial' }, }); @@ -1759,9 +1686,9 @@ describe('ModerationConfigService', () => { ); testWithTwoItemTypesFixture( 'Should return a partial item type if requested for a selector with a version', - async ({ itemType, newItemType }) => { + async ({ sutWithPrimary, org, itemType, newItemType }) => { const fetched = await sutWithPrimary.getItemType({ - orgId: dummyOrgId, + orgId: org.id, itemTypeSelector: { id: itemType.id, schemaVariant: 'partial', @@ -1772,18 +1699,13 @@ describe('ModerationConfigService', () => { expect(fetched).not.toBeNull(); expect(fetched!.name).toEqual(newItemType.name); fetched!.schema.forEach((it) => expect(it.required).toEqual(false)); - - await sutWithPrimary.deleteItemType({ - itemTypeId: itemType.id, - orgId: dummyOrgId, - }); }, ); testWithTwoItemTypesFixture( 'Should return latest item type if only an ID is provided', - async ({ itemType, newItemType }) => { + async ({ sutWithPrimary, org, itemType, newItemType }) => { const fetched = await sutWithPrimary.getItemType({ - orgId: dummyOrgId, + orgId: org.id, itemTypeSelector: { id: itemType.id }, }); @@ -1791,10 +1713,54 @@ describe('ModerationConfigService', () => { expect(fetched!.name).toEqual(newItemType.name); }, ); - testWithOneItemTypeFixture( + // Fetching a *historical* version needs two versions with distinct + // timestamps. `item_type_versions` is a view over the system-versioned + // `item_types` table, whose `version` is `transaction_timestamp()`. The + // rollback harness runs the whole test in one transaction, so a create + + // update there share a timestamp and collapse into a single version — there + // is no older version left to fetch. This test therefore commits its two + // versions through the real container (separate transactions => distinct + // timestamps) and cleans up after itself; it's still self-contained. + const testWithHistoricalItemType = makeTestWithFixture(async () => { + const { container } = await getBottle(); + const sut = new ModerationConfigService( + container.KyselyPg, + container.KyselyPgReadReplica, + async () => {}, + ); + const { org, cleanup: orgCleanup } = await createOrg( + { + KyselyPg: container.KyselyPg, + ModerationConfigService: container.ModerationConfigService, + ApiKeyService: container.ApiKeyService, + }, + uid(), + ); + const itemType = await sut.createContentType(org.id, { + schema: dummySchema, + description: null, + name: faker.random.alphaNumeric(16), + schemaFieldRoles: { displayName: 'fakeField' }, + }); + + return { + sut, + org, + itemType, + async cleanup() { + await orgCleanup(); + await Promise.all([ + container.KyselyPg.destroy(), + container.KyselyPgReadReplica.destroy(), + ]); + }, + }; + }); + + testWithHistoricalItemType( 'Should return requested item type version', - async ({ itemType }) => { - await sutWithPrimary.updateContentType(dummyOrgId, { + async ({ sut, org, itemType }) => { + await sut.updateContentType(org.id, { id: itemType.id, name: faker.random.alphaNumeric(16), schemaFieldRoles: { @@ -1802,8 +1768,8 @@ describe('ModerationConfigService', () => { }, }); - const fetched = await sutWithPrimary.getItemType({ - orgId: dummyOrgId, + const fetched = await sut.getItemType({ + orgId: org.id, itemTypeSelector: { id: itemType.id, version: itemType.version }, }); diff --git a/server/services/userStrikeService/userStrikeService.test.ts b/server/services/userStrikeService/userStrikeService.test.ts index 3dbb5cba..c82aeddb 100644 --- a/server/services/userStrikeService/userStrikeService.test.ts +++ b/server/services/userStrikeService/userStrikeService.test.ts @@ -1,237 +1,241 @@ import { uid } from 'uid'; -import getBottle, { type Dependencies } from '../../iocContainer/index.js'; -import { type UserStrikeService } from './index.js'; +import { makeTransactionalTestWithFixture } from '../../test/harness/transactionalTest.js'; -describe('Item Investigation Service', () => { - let container: Dependencies; - let userStrikeService: UserStrikeService; +describe('User Strike Service', () => { + const testWithStrikes = makeTransactionalTestWithFixture( + async ({ deps }) => ({ + userStrikeService: deps.UserStrikeService, + }), + ); - beforeAll(async () => { - // The mutation should be ok here since this is initial setup in a - // beforeAll; it doesn't involve reset state for each test in the suite + testWithStrikes( + 'Should properly calculate strike counts for a given user', + async ({ userStrikeService }) => { + const fakeUserId = { id: uid(), typeId: uid() }; + const fakeOrgId = uid(); + await userStrikeService.applyUserStrike( + fakeOrgId, + fakeUserId, + 'fakePolicyId', + 1, + ); + const strikeCount1 = await userStrikeService.getUserStrikeValue( + fakeOrgId, + fakeUserId, + ); + expect(strikeCount1).toEqual(1); + await userStrikeService.applyUserStrike( + fakeOrgId, + fakeUserId, + 'fakePolicyId1', + 1, + ); + const strikeCount2 = await userStrikeService.getUserStrikeValue( + fakeOrgId, + fakeUserId, + ); + expect(strikeCount2).toEqual(2); + await userStrikeService.applyUserStrike( + fakeOrgId, + fakeUserId, + 'fakePolicyId2', + 10, + ); + const strikeCount3 = await userStrikeService.getUserStrikeValue( + fakeOrgId, + fakeUserId, + ); + expect(strikeCount3).toEqual(12); + }, + ); - ({ container } = await getBottle()); - userStrikeService = container.UserStrikeService; - }); - afterAll(async () => { - await container.closeSharedResourcesForShutdown(); - }); - - test('Should properly calculate strike counts for a given user', async () => { - const fakeUserId = { id: uid(), typeId: uid() }; - const fakeOrgId = uid(); - await userStrikeService.applyUserStrike( - fakeOrgId, - fakeUserId, - 'fakePolicyId', - 1, - ); - const strikeCount1 = await userStrikeService.getUserStrikeValue( - fakeOrgId, - fakeUserId, - ); - expect(strikeCount1).toEqual(1); - await userStrikeService.applyUserStrike( - fakeOrgId, - fakeUserId, - 'fakePolicyId1', - 1, - ); - const strikeCount2 = await userStrikeService.getUserStrikeValue( - fakeOrgId, - fakeUserId, - ); - expect(strikeCount2).toEqual(2); - await userStrikeService.applyUserStrike( - fakeOrgId, - fakeUserId, - 'fakePolicyId2', - 10, - ); - const strikeCount3 = await userStrikeService.getUserStrikeValue( - fakeOrgId, - fakeUserId, - ); - expect(strikeCount3).toEqual(12); - }); - - test('Should only apply strike for most severe policy violation', async () => { - const testActions = [ - { - orgId: 'fakeOrgId', - action: { - id: 'fakeActionId1', - name: 'testAction1', - description: null, - applyUserStrikes: true, + testWithStrikes( + 'Should only apply strike for most severe policy violation', + async ({ userStrikeService }) => { + const testActions = [ + { orgId: 'fakeOrgId', - penalty: 'NONE' as const, - callbackUrl: 'fakeCallbackUrl1', - callbackUrlHeaders: null, - callbackUrlBody: null, - customMrtApiParams: null, - actionType: 'CUSTOM_ACTION' as const, - }, - targetItem: { itemId: 'fakeItemId1', itemType: 'fakeItemType1' }, - matchingRules: undefined, - ruleEnvironment: undefined, - policies: [ - { - id: 'fakePolicyId1', - name: 'testPolicy1', - userStrikeCount: 1, - penalty: 'LOW' as const, + action: { + id: 'fakeActionId1', + name: 'testAction1', + description: null, + applyUserStrikes: true, + orgId: 'fakeOrgId', + penalty: 'NONE' as const, + callbackUrl: 'fakeCallbackUrl1', + callbackUrlHeaders: null, + callbackUrlBody: null, + customMrtApiParams: null, + actionType: 'CUSTOM_ACTION' as const, }, - { - id: 'severePolicyId', - name: 'testPolicy2', - userStrikeCount: 2, - penalty: 'LOW' as const, - }, - ], - }, - ]; - const mostSeverePolicyViolation = - userStrikeService.findMostSeverePolicyViolationFromActions(testActions); - if (mostSeverePolicyViolation === undefined) { - throw new Error('mostSeverePolicyViolation is undefined'); - } - expect(mostSeverePolicyViolation.id).toEqual('severePolicyId'); - }); - test('findMostSeverePolicyViolationFromActions should return undefined if no actions apply user strikes', async () => { - const testActions = [ - { - orgId: 'fakeOrgId', - action: { - id: 'fakeActionId1', - name: 'testAction1', - description: null, - applyUserStrikes: false, - orgId: 'fakeOrgId', - penalty: 'NONE' as const, - callbackUrl: 'fakeCallbackUrl1', - callbackUrlHeaders: null, - callbackUrlBody: null, - customMrtApiParams: null, - actionType: 'CUSTOM_ACTION' as const, + targetItem: { itemId: 'fakeItemId1', itemType: 'fakeItemType1' }, + matchingRules: undefined, + ruleEnvironment: undefined, + policies: [ + { + id: 'fakePolicyId1', + name: 'testPolicy1', + userStrikeCount: 1, + penalty: 'LOW' as const, + }, + { + id: 'severePolicyId', + name: 'testPolicy2', + userStrikeCount: 2, + penalty: 'LOW' as const, + }, + ], }, - targetItem: { itemId: 'fakeItemId1', itemType: 'fakeItemType1' }, - matchingRules: undefined, - ruleEnvironment: undefined, - policies: [ - { - id: 'fakePolicyId1', - name: 'testPolicy1', - userStrikeCount: 1, - penalty: 'LOW' as const, - }, - { - id: 'severePolicyId', - name: 'testPolicy2', - userStrikeCount: 2, - penalty: 'LOW' as const, - }, - ], - }, - { - orgId: 'fakeOrgId', - action: { - id: 'fakeActionId1', - name: 'testAction1', - description: null, - applyUserStrikes: false, + ]; + const mostSeverePolicyViolation = + userStrikeService.findMostSeverePolicyViolationFromActions(testActions); + if (mostSeverePolicyViolation === undefined) { + throw new Error('mostSeverePolicyViolation is undefined'); + } + expect(mostSeverePolicyViolation.id).toEqual('severePolicyId'); + }, + ); + + testWithStrikes( + 'findMostSeverePolicyViolationFromActions should return undefined if no actions apply user strikes', + async ({ userStrikeService }) => { + const testActions = [ + { orgId: 'fakeOrgId', - penalty: 'NONE' as const, - callbackUrl: 'fakeCallbackUrl1', - callbackUrlHeaders: null, - callbackUrlBody: null, - customMrtApiParams: null, - actionType: 'CUSTOM_ACTION' as const, - }, - targetItem: { itemId: 'fakeItemId1', itemType: 'fakeItemType1' }, - matchingRules: undefined, - ruleEnvironment: undefined, - policies: [ - { - id: 'fakePolicyId1', - name: 'testPolicy1', - userStrikeCount: 1, - penalty: 'LOW' as const, + action: { + id: 'fakeActionId1', + name: 'testAction1', + description: null, + applyUserStrikes: false, + orgId: 'fakeOrgId', + penalty: 'NONE' as const, + callbackUrl: 'fakeCallbackUrl1', + callbackUrlHeaders: null, + callbackUrlBody: null, + customMrtApiParams: null, + actionType: 'CUSTOM_ACTION' as const, }, - { - id: 'severePolicyId', - name: 'testPolicy2', - userStrikeCount: 2, - penalty: 'LOW' as const, + targetItem: { itemId: 'fakeItemId1', itemType: 'fakeItemType1' }, + matchingRules: undefined, + ruleEnvironment: undefined, + policies: [ + { + id: 'fakePolicyId1', + name: 'testPolicy1', + userStrikeCount: 1, + penalty: 'LOW' as const, + }, + { + id: 'severePolicyId', + name: 'testPolicy2', + userStrikeCount: 2, + penalty: 'LOW' as const, + }, + ], + }, + { + orgId: 'fakeOrgId', + action: { + id: 'fakeActionId1', + name: 'testAction1', + description: null, + applyUserStrikes: false, + orgId: 'fakeOrgId', + penalty: 'NONE' as const, + callbackUrl: 'fakeCallbackUrl1', + callbackUrlHeaders: null, + callbackUrlBody: null, + customMrtApiParams: null, + actionType: 'CUSTOM_ACTION' as const, }, - ], - }, - ]; - const mostSeverePolicyViolation = - userStrikeService.findMostSeverePolicyViolationFromActions(testActions); - expect(mostSeverePolicyViolation).toBeUndefined(); - }); + targetItem: { itemId: 'fakeItemId1', itemType: 'fakeItemType1' }, + matchingRules: undefined, + ruleEnvironment: undefined, + policies: [ + { + id: 'fakePolicyId1', + name: 'testPolicy1', + userStrikeCount: 1, + penalty: 'LOW' as const, + }, + { + id: 'severePolicyId', + name: 'testPolicy2', + userStrikeCount: 2, + penalty: 'LOW' as const, + }, + ], + }, + ]; + const mostSeverePolicyViolation = + userStrikeService.findMostSeverePolicyViolationFromActions(testActions); + expect(mostSeverePolicyViolation).toBeUndefined(); + }, + ); - test('Should properly calculate strike values using getAllUserStrikeCountsForOrg', async () => { - const fakeTypeId = uid(); - const fakeUserId1 = { id: uid(), typeId: fakeTypeId }; - const fakeUserId2 = { id: uid(), typeId: fakeTypeId }; - const fakeUserId3 = { id: uid(), typeId: fakeTypeId }; - const fakeOrgId = uid(); - // 1 strike for user 1 - await userStrikeService.applyUserStrike( - fakeOrgId, - fakeUserId1, - 'fakePolicyId', - 1, - ); - // 2 strikes for user 2 - await userStrikeService.applyUserStrike( - fakeOrgId, - fakeUserId2, - 'fakePolicyId', - 1, - ); - await userStrikeService.applyUserStrike( - fakeOrgId, - fakeUserId2, - 'fakePolicyId1', - 1, - ); - // 3 strikes for user 3 - await userStrikeService.applyUserStrike( - fakeOrgId, - fakeUserId3, - 'fakePolicyId1', - 1, - ); - await userStrikeService.applyUserStrike( - fakeOrgId, - fakeUserId3, - 'fakePolicyId2', - 1, - ); - await userStrikeService.applyUserStrike( - fakeOrgId, - fakeUserId3, - 'fakePolicyId3', - 1, - ); + testWithStrikes( + 'Should properly calculate strike values using getAllUserStrikeCountsForOrg', + async ({ userStrikeService }) => { + const fakeTypeId = uid(); + const fakeUserId1 = { id: uid(), typeId: fakeTypeId }; + const fakeUserId2 = { id: uid(), typeId: fakeTypeId }; + const fakeUserId3 = { id: uid(), typeId: fakeTypeId }; + const fakeOrgId = uid(); + // 1 strike for user 1 + await userStrikeService.applyUserStrike( + fakeOrgId, + fakeUserId1, + 'fakePolicyId', + 1, + ); + // 2 strikes for user 2 + await userStrikeService.applyUserStrike( + fakeOrgId, + fakeUserId2, + 'fakePolicyId', + 1, + ); + await userStrikeService.applyUserStrike( + fakeOrgId, + fakeUserId2, + 'fakePolicyId1', + 1, + ); + // 3 strikes for user 3 + await userStrikeService.applyUserStrike( + fakeOrgId, + fakeUserId3, + 'fakePolicyId1', + 1, + ); + await userStrikeService.applyUserStrike( + fakeOrgId, + fakeUserId3, + 'fakePolicyId2', + 1, + ); + await userStrikeService.applyUserStrike( + fakeOrgId, + fakeUserId3, + 'fakePolicyId3', + 1, + ); - const userStrikesForOrg = - await userStrikeService.getAllUserStrikeCountsForOrg(fakeOrgId); - const user1 = userStrikesForOrg.find( - (it) => it.user_identifier.id === fakeUserId1.id, - ); - expect(user1?.strike_count).toEqual(1); - const user2 = userStrikesForOrg.find( - (it) => it.user_identifier.id === fakeUserId2.id, - ); - expect(user2?.strike_count).toEqual(2); - const user3 = userStrikesForOrg.find( - (it) => it.user_identifier.id === fakeUserId3.id, - ); - expect(user3?.strike_count).toEqual(3); - }); + const userStrikesForOrg = + await userStrikeService.getAllUserStrikeCountsForOrg(fakeOrgId); + const user1 = userStrikesForOrg.find( + (it) => it.user_identifier.id === fakeUserId1.id, + ); + expect(user1?.strike_count).toEqual(1); + const user2 = userStrikesForOrg.find( + (it) => it.user_identifier.id === fakeUserId2.id, + ); + expect(user2?.strike_count).toEqual(2); + const user3 = userStrikesForOrg.find( + (it) => it.user_identifier.id === fakeUserId3.id, + ); + expect(user3?.strike_count).toEqual(3); + }, + ); }); From 919c6d06bb5112da8cddc8b9d15b4f611ec9da32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tao=20Bojl=C3=A9n?= Date: Tue, 14 Jul 2026 10:50:27 +0100 Subject: [PATCH 12/57] test: Add MRT video playback E2E coverage (#890) * test: add MRT video playback e2e coverage Co-Authored-By: Pi * test: use Playwright base URL for media fixtures Co-Authored-By: Pi --------- Co-authored-by: Pi --- client/public/e2e/test-video-2s.mp4 | Bin 0 -> 25207 bytes .../ManualReviewJobContentBlurableVideo.tsx | 2 +- server/e2e/fixtures/media.ts | 12 ++++++---- server/e2e/tests/mrt-job-review.spec.ts | 22 +++++++++++++----- 4 files changed, 25 insertions(+), 11 deletions(-) create mode 100644 client/public/e2e/test-video-2s.mp4 diff --git a/client/public/e2e/test-video-2s.mp4 b/client/public/e2e/test-video-2s.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..701f77b7c533c30be96b78c30832b62f8a906d0c GIT binary patch literal 25207 zcmcG!WmH{D(=NJjCur~lcXtg=@Zj$54gnG*Sn%NP?(P;KK#(B8-GjRZU3aeRy}x&# zcYODZ`{y=1tGcVJdsffxs;Dgb5H72=f1*@P8x#_J5^C{v-Ln#b7`nm|Qm(V_P6o&&~GF zGXdWJguiP8p8Mb7zn=5IJ~sy_r1PgBrMa1#D-a_#w{vy-!}+5rq+Z0ob%r6ZH?uYd z@<{E?{=4n809y&9-uI^@la-mB%inc?Jgm*k|A|AW29S1?Hg+(xGl$4P_t{%JSO5_m z5Boor{#$KIv%m6)&0NeOGLQhtM|F3xBmFB59pvg}Vh6;#UEN&&=@5ua4W!cm3JL$N z^Dj;g&}?ZS3IQZQ;mQN@-0aNkyv%H@?4)+qCZ6ovoPTBh`Nao=0m=e4M}OKWRu3QtnS( z0R8_v9|Q6{=)a#w3+QeNVE_T({n7uwDWpwifSfl(=SpQ)P)GpY84^$jZZuHsEimR! z01*wKh&~8J_Y(wSTmXu}1Dy@j$p;Kxp%M^CWE=#NECzvOxIrKVB4DrsV@|CH1k!2* z+Uop&F;GZ*f$)DAC=?Wg_2&l?`8)hqTI3)8U-bWCpg`aLDGRgz|8Afa5+EqO(ErUq zT^-#W{{1LbAZ_*^1BFTW+dwf~|1!{jWdA=HCydtL#_9WC#_0ffQ{%rY`cJc=Kz~LL zpeiJAo4cC*8Tc^%{{B?J_<;o|o(N9q-^@4KyFH_M1)B$w-)Ao2kt_8pzfJ*@F0LlRX!xl6H z;26LKfH44h0OkO60GI>-n7|(jNdrR~G!Foh4=GyJ=Ot0m)54etV5QqaE7`)bi>j(v0$5#+Y z>=WQR4gl8yxEKXd5J))=a2*$b>wxqLp{u2n859t4akR4o2+)^+--9?lVpkJO$fWU4 z4n#-`0EC*jo4FYSRL9=@4+WwivVYIIfFp1*c5;H$_&-s?^QV5#W-T8UHB^ zsSpHAe-^;>#|HefvIA*E3m0>95J=n79*_dms)C0DgUgmB_d9nMq??l%=SdeRPEH|n zBqzC@4w3W#S|i)er|H&CE@K3~@&% zFFSJ!H&S+1Rt_e1R(39cY31hT#LvR={{4Gq$g087&e(z3(Z!Ml@)Tw(H+wrk#?i^m z+R?$4pVZXY#Mo4bjTBfJ2yu{_nVZ--n%WAn@w4)?k{UZ0+j+T~3$c1~@Uwcdv2l>v zn+sW)dy=}kn*cE$QYTk0peXQb=wc?s#>@&70)9yCtv$`n3?VhL0VNDwj2$e^h1j@B zO|4uU?TrnAqHLsYF6MT2)~-N|*OS-G)D4K3I@=4e0xB4rc{@6o3$e2?vayj`7`wU| zI=R|fJ3%D=s=(RF(9y!e)!a>piJjEV$^|Il3Mg;q=xA$f1tbjr;jxjr+F6?dy8Odn zC3SH5^9)mKdt*0<9@Y+S<}P-|fDphpv2%AZ_A)eew0AOg1Jb6zD+1;uYX_hTK+we) zB4goVY;O*Eo{6E87ZA5L69W3m(Adn_3DOD^LlbLbSI9%GUCjTq>%FSFF{ z1r&ENHT<9ELh@Zqg-l&6NbP|YkvXJUKthO}gPE1o8PXUbR%UJ>>IA9y4>k4_;^qSw zu5RW|LY$=5PCzFC0|Mwnpe|!)p#LGg3GxSl;J=!O27xfa&$pqk*+(2x7+&L*mQjMV zWH{n)W4aE^8#h#he&+{e&%QWbmcOlT5nJ{7oMJx{s-Q$zGh8Y={1IMI^KQ@rrtxrT zp*W2BrMp#)jqOU{q3PsU4GN{zt5?c6$2wm+N~VKuQi`6VVXbc(JsL%xdM23ZXEn}B zUt8;tdaum;@YEfnNK1VrY_ma%7>ST^R_LUn6#M4g{Zi9Me?{~CJyl8XvIKVTMGA{J z>tLeMMIt56R)?sBxQJ6i&srov3H)g&cZ4Z;OS1_PGuTR z@?_x6z9%kxLZ~_Z@>5C987cq7udcA&#N+(eX9S+=`1)@J79SXFSi8RkljJZfm~W@) zve)XQk5cNMMuvDO1XeO$_6{AU;ihrch8&!i(J=V9zB}y~=TeVC!@AJ|+ehoxsxTe0 zXU2qOn-oM&WnVEGs`myO`rZG;cW;{@Ab|G;H+fYfAoTQ#X^9;9v;Yugkq#s6rNAkO4h{JmkO`=W;EZN}Iy?FzIXaiwH-4zypN=e{LJ z?1xi`j`_>r-ne#N&&pLy%&Bgw&(2c0c0Y=T2}NCM&JrCW6LB-5o_m12%Z&bcTT$+LUG z`L;u#mFDUqMW$qSbj%Kn>;Hz*$=n~6`DyDg0OP~Cc6x&iU-p?GqOGvGjW84sj!H7A zu|@n&QOovp4;NIz7_H_(E>Q-m1^+RqzH`YHlUBuM#ntgP*OR6>LPa4cm6goFt;N1o zzSgG7kFf)}ysU{(DDAMtvu&a)CQD*wAgyMo6l1iUE92VvQ~me46kt->$b>Y9W_0c`)tQ2Tb862gH_pSzs9U z4A}ofM8_&47AA3H;inJ#&dB*@2=<;p6HT->S^mWaJ z+E!qi(8w!O=9mv3H($C`N%%EuIS=q={H0x@cSu3m-sJYDc^WeoS_Mrkp zHd3;NVId^vpY?oYh%hlgc#RlOZ-?`kIB8?`1R5zKpGKmRQdUbO4p2Md3g zy%1}5Yr(h?)}lOrDv$5?b-$`((KttAy?vfp?^@9&@5rh{*+1ONE&AraZ4{W)eXj}k zQTq!vk!0)TEmJg!_@*2fA*RVEa_e|RGJkisacxW1X+Dnm$@#VIO5u&s8LQMU4}*zM zTI}mDGIMjp2Tw?&k-v)R$b6=7tom61&gOgcMg7R^6^_n~R63R|qfon9^!7ztOiP+yBCOT&=f|kF#%AJFm32aw+raal4_ctF_q|Q5k`JnHFG$wtm zpITY^+Bi7HY%CyMZ1A&e_yROqyFj<;p^YKa;IelLJi8_j`1!fUxou>>!YX3<@mKs-c_0v0;&qS0(ve z8GFL#)_gTN1Rs7bgjMq4)-y`xAoV?txnJqk&ZVJDY)8-3*xC6T8LuLyfK#0j++Re) zx}Wq(Y7=P$p6r=xi*G&D*NMqFvK72sqe}vLT^#Vkt70M_`GJ(31={ezors04p+wn9 zf^{WEFG2A%iYdnjVy5@(97mtNy^l*)w>>Qlamkz@NRO!ckXad_owQCN`2)|)=JKpQ zLi+chQ}bB<^ZMj%0a3(Uf=B=|cLVN%Y?)L2bVQA_2&i&2n2jJ;YT4dg`Xod{!<;uN zkW!aX$7|bYp-4@#5t{vzJPe1K$NMJE<1^$Cg*TajxQTax;^M63OSHOO-}uh=&275s zn^`7w#f~{a%dO%4oKvNJ98pdUj*5od+8P^JkG5LH-wqs?hj!?DmGJgsRHiey6~#dp zxUZH>yXYgdge|9kzWF4^=Y5-ua|Y7*erLToyU#MQH-&FvpX2*nO40$3Z}65zss7PK zD@Esy@mJ0M8((5g5M@fRfeNW-?BeA5lb5>f`6s*#9=~Sa2*sdLdR~}~EUMRLtI&`& z`G1b2K+r}Nx}{1k)Pl5gF)kvH~L{foLm%tIt0a;wm7NKZPzbP{X;F!ZAGDU_fYejW0d~f zbB6C%O}N!!@Z{zFqZ484S8(&(=`mUP7C)9gEZ*-lR@U=h-9GV(M=$9+~#G%ep*tA6`~jwjk<9#lnJ`gSD-F(*mcC zBwc+feOZM3fstc8jGs0VJS8eB7MAQ~AQ3kfmKqTcJUl=(Zus&Bl;WC=NCR2WUz#R| zKIr>-`f#f!`l{xMDJpOdX}DMisI06*y}V((YGT7PI2C$!eY8E~>YZ<(y>iz|ht!YpeIJv5kRwA4VP>qC zNQ046jtd$nYw(eY$+dCqmOav1&q_aW8;ka{+VN{>yzwr7qZc)Q8u3DoKysOva6s@N>61$$}8swovan9=4i{X!c(EtZE9;}mj< z*aFA&e5L;mPoSeq_g<+WC8WgYGc@l@3dBT4t7rvk2VU0JzEEm&8a%`TJgL0M)3d8I zWXiCIA?QTB7|#VYbPQC+C=(a)PtYT|ukgCb(B0Q;GUt1LxHyfyd_-(W*~1H&ujR*R z?QyeXL{j3BgXvBB(y4S-=!%Kb7WSd2UkffZh-I9rHV^ah_qjh~z!w);^cuA$BOX*Rg zF&gdQ*HYLxFr+U_!!x)IlG`SRhx0I5+~Kmal3=C9z6s=xO;l*^_-{r&{ z@(=GqKfzWj75`SHOq$x{>we%h|62$FOfu4)%n!Y&f8vz{*K$fTh0nnfBpXZcQRmJG z$++51{S=yzYU@t4xn;gv?#If^s_L@=Yt5Y;{A}gPL2LC?5KZFsms&l3_P+8K_L9fy zcMwN|36*8+ChiAi6Eyl0u4}DX;SFe+oAH>FIv6QeWKW4-^V|pgRPFcd`)pgv*kv}* z%v@%MuBg@Hrfwlo%2!l+@Rw z1207F;2exdDy@-EJ&g1O{qjokWw0+u)7x@{WUmBa0(huvkusBKhN`AH7KiN9Y(pNd z&wN|@860(kcqs_T#z>n>6;YV`V=BZeir&OK_y!Zx%c_|3%ZVEwc*dJuF_dij)yh7W-~&_whsb~$#~yZS@&=YprYBfJX}!goTd>Th=pgh z{mQ9Pt)PpNzp-{QYG8N+SQV3mCGr3-x2J1vvC~a9HmpGE_|R&ZD4S%3VQft zZ#=thJ|xW_7e-helXU1N@AO@#?>o-tmN!B|?h@_W+5D~n!DW|m{tO@;6-Q<HG0_RBG++Yu&Lqo zlP^t^Y8Qh{7p0$tfjW6=doBXud85EP0i77V$YLXGKH?7@_*zBt35zFN#8079Ys^d3 zWzov4YK@XP1=#gaj?9azn;b1zBJ{p~^YVM(?H50X7p%EQopDMooKT+~X@W|#w^THI zHibn8Uxu*~Q$_ir1d)zl6sp}29+z&blBN(Nktibl28KN@RFw|T2VZ&s>OgiER}-qP-lg-b~b=82i(EQG2%`uz2M_DWmY(^BKfcaOIHs{66V zif#U}Wqlb2%Px#gMNqd#U@yfgPW{`J3)fn;;n6lfc-rB+JVcwQsat_IZ2XTDleQzL zj==_HQzV__63PMnzxO^pVCv`*NhxYeqz%sOLCZZaWPuO`b~{3n?u0t}y8`+b+GFXw z3NjD^P@rVnzu+z)8bnIapa$KH2C2tIQk8FVFICEtH>u0(rA_3Lv&S$gSUSK9(6bvy zl5J*lt)S{fl}hW%cRzwwxtUNK*u&6rN%xezG0}qpbA2<$!7rA;TU#8ZgbIzR{pLG$ zDmgCf3nKT)(;7+dNLZBHq=VvmiV~<(Z=R!kbV}l-N(<6t%2LlgJWwS`3d{=#Cg!o$ zu*F4=f9N+Ai^^Smqy={!@jrRrI_Do#*yE-rsuE;aJSy}y3J_1q_`z`O<$i)DBc~u0 z;Bd`Q+=tWt5&$CsOS3%e7E#M&cQ6s99oGtdpw{P^+WEA3DJCr7iA`4C+=T!K4@*ct4G1)bz(fr{vTA@blc15+XDjL(H*e;ledAcAmBzsfM|`M>p9U+aIO9RXKI&<+r`{ z!atoUz($PXA@VBGW(98_NtUzFpO&T`)iijCzXK~<7Y%8Nu{Vb_gZow@9bSr+2Z|On zt_h!i%{Mi>pM|d(vPc$^PQM=}bNk|QK^Ov-e5J6{vj~=f&e1V$>y8cei>a?%c_rih zlHriM12lQU3okP93QP4IvBH?n+WR}E%NuC-?HWt3U}dk8D;PE!ts|{6c3O>&gh&1v z?pIKHL5EaZf%1_FmtiG{Z5M9Y!xr=&fsYgtnn7 z8-(E$beK#JsK(Ee3@MsRUO1h3NgYI3OtS&atRX0^E=5dy1oz(v9S^#`i0*ajpcx0M z+$bAmc~`)F*Uq+3M;fceqxhZ{_!*uvuk|+_LA>tk9RN*wWp5%d=cDhH)aR0zl*Ia$n(Qn;M zk6CwuzR24k>906j8^gC@X6E(m*#@YJ^q4+e5WF;*4!;M%|az$NdwO ztrFH@o2?Q#7?eg{C9+34IF11mKGqD!+K$RalU$_KL_K6b2(@_FJ~g3U#0>q4WBj#K zSettLGt#OYH|7=g8h9HKUn&Ylz4aw40d`8jC06y!WNvOjsT&55Kd=;JVp71yge!h; z00vure+FMt*R?on>s0P3J(kM&)8c1ByL}#Y%fEbX6KW4YLI~jc{cG#jIOB$q^kjjY z#wQO3+Dv$N#8jr|6;c0Twh^I(elyG{ZMxk$$n-ibxL;(v9G&k|GjIq_D?v)0jrG6aJC}V(CAI#8^~5sn|%`JvI(37Bo}DKxLpQFe7kMhICP% zihHISg799CydSB2n%&Rb>}{vvfKvH0QJtxA1SbR=0Sj{yLbwNY@Y8rRSa`hYshNzY zRJ|XUPL)aa6C%F4N~g>9VCw?S3V-8!MCh%)-5tZQsUKSvMB|+^z;+V#s-0qqhOUTB zcyh?QyL1NQ)lYf#H+tIml-^L{$Q4Zs>>#lwxzM-pO}s)>HoO=zmY*UwFFR*&sXS%y z3Okcziw1E?@SIo~SR19Z9(8u4Y6p*kVAh~(o)TM~zNBAx`+cD^m#4NrxqeQ$3T?Cf z>QSi9Lp9iRL>eojQ8SXJ)pHV0q9FB-k0Jk8jF7ioZ1FJi?<4Q6x?0BZ%l0mo{O{1b zkZJjtlMi3^derZ!1H87cFJt05&|y=jmR?s#uaY3}c`BcPpsgTK%aq1yo*(}*HOtTA z*nO>C-)&C8ZPvk2h9XzxHr`ly_bpk0Gk9)9e$yG2JbosL#AdfG-{gU<*1EP>*jGpIC;h{U zHG$=pl4w?lBqpN(7)std#Q^OMak&|DOcrcS4~R64#aR)fY5d2L+s{SAWweT-@+HcS z9wwF1Jznw&0_hA-?)}E>{(YI(R%S291PDtGVrFN7i+5mCdW@?W43jImZG4u!h%mAiJTyPVso+Mp>&l}2iG%(qw8CZ^h^|5eKQj>IPW1 z_G#Ru->oZ)$bv$ZOa%xC;TLif;TP?KZPE~P$nD8?m>z3X4bvaMv*Mnw7}Yysksm>q z*^8}}3?B-98+dA5JzA~5u9(fcOr!Na8By+4P+Anq5fiA*05$@ETM57u`!tc0!mrsr z2VNL|^6hNisU}QJEv;BdB;M@`mWEMnko&0?tb8|BGe+H*2Z@~6SBD8D zsE_tvhY4{>a0Z0f?|I)^m{ANGN(f%#Qr*j6a_qMxl9uJfF2;&$jCB7QNNU({FY++< zr1|l_zIFuHuRZ&HO*>>!_fjwBpIzE=Q7L_KgM(Q@A+l@5&X`O6vKG2JCOOIP;XC7F zXR4)3Z}0Lnlk-a`|1sC{mdy2p6BlUQj{bU&9e28SMYSSP`rj5avWU|1|#H&T1oTcpa;VgdM<%aeuKyFIv<@Eo#%q+ z)whd($zmkH)rWC5Qpr=*6w3vd<3T_)-;qaDX%#( zQaL$tpes~F-^8h~x*KU{)0UDD<8@o4wq-Vr^TOK>Fam@y!POC zh;_5^aZ!C4uDzVyKZk^^F0Xx)hr86YVWc7XIM+6uxP3*DOeEq8a>!xG%dwJl zD>xySPV(GsHU|#%#8qpP-sJ~r6+6w%S$D05Fgm&l@9}Ysc5PJ-uA^0Wy%QzM+YBIH z``$LuxXBQ~A z@Vyf?jW6_epR+jC}=C&*>^adj4$g+WNZ8fojwbdNI+bO{|>iTZL8@|Sq z6W9Mxm~P*XAD2b9lVvp?BprsG&&!IB>@wht0DNKEONkRY$ZN#69i%UoV2U55-xRd* zDTvDqLuvE2QA@ISPd;61+lUVNh9`Ddyua#Fh{a6`?sdJV!GM) z4GvotqF19R@hsRV{x0gU^!~kQ^rhQ^6Ulp4Qkp78=n-<5>60&C|78cokgYyo2U9uc zT22Ow>AwEJAr}*B2?`s#l13@ageRb?~^WeM5Q>IgY2M#JMS%+$72j3VTd< zoe4n{iLomC@{o1~$w|bZ2}Q-Ho)4}bCF7`NTqYE>w%otS(fdBH_ySl=X{SECymLS?2T@TlEQ^llCm$y80)6%8q}52CSk`y zR$eHm)GNq7%4ibd*j~)Ys>^VO@~(g5=!^O^{SA7y)^)kh>;)7XQDGi;)T{6)Vp!m- zG0gSC95k674ZK)~IHsy~V<>wI)oDlLMVf%ssVXK*Z#g`DBm|t6Qr)QL%r3unz4Joc zAn!NS1uGjdDzuzC)fY`2c9(}!WFIZvPD-8fjzl;jRN34@*0B#AyajkTy0BCYod82( zV&ZgwY|U-6!4Y4dzE1L|Z?^67{IpS1$%{*MBF?2yu87T%vb$KzUV4wFI~eXEuoCH$ z>ZkvdtljCLQimGt??G3h%R=6O*~dH~ZGvPI%kNLLQ~GFU7QjFp9Dpo`Qb3??4?i0| zAE)!p8{fo zyBHTnL3i@y`hMyWkAZ1YaOQ0KKTuMSK7?k9E7 zJYS^y#x@`@o|ryvc?>I~gd~ z%>&1&^uQ*PPO2#@iy)9{fK6cf{rNXsTiKmho&NsNIeRm~$9{6f$nt>_W{gn5sHrFFU$CQQig3v}Uobxi1$P zVcaDO+$_Wq3M;y=h1Umf?n%_VUAa<%$)b#pmz?OZXzQm>rx%K2*pv-XE&8Evp4^9` z;3mkui3ohn2n{QmRsoz>*a7DiWZ>sAbtGF{4N}Yr==M_Z+5P2Qte<@pfUqxxrVIb_#5*o^j67XFU z^UHp7@3-D_)$sBr6xZ*v@fdlbhe?QN=yDT&B2b$Ik-iK=9|)NH zkfeQCsT=Jqioz00%z5^$V9xTD8im4!$nsVLbqB8*O5PfYZgTS?6Vro#6D|+Il@KhO zuHhJA)X|wizWscq7K=iaSsPFC2gtQOHJpAjC#r-Sm%WWV*EY}V_zqw`!&`0;VW zXTbbaME)qL7SShH&DQZe={ylPKciy`BQ(K(A>7@Cl7rmO0l(-~{QCK#?#!C=l?b8O zxd3%SZL06MUt!(etFmUqE!Wv|*qDn+$~}Et^&mz~t^%sclkmzWHN-J0y(K5>^!|sa zK0Y4>tF%_{ye2BXasNH8c@eDsfmAj@zt3uRsG!X8)lu{uJ~tbg1FU96kO4I<37G8w zc2i`qV8}iQi=(cu*azxc&-AMs6ayLC;&QLKH%JM>?Z!jes;NY(Zzo_QGkQi(%<>l3 zISaj|u9oF+{@m0ZRqKcU^y+R?e0+CMsx9k;kJ3V)kWL?b<3O!Au_s zp1^h9(CnVB%;^hfb)B>=EX=1#xiTEGTiO;e*BUG3PmfI)v%{nWTezm&o_WAKhkDOi znKmTg=Y>iPoO|_%@g*1ZlSPg>>bd)liv4<#dD|NR{^_-a^;_bivzp41c0D%zqD*In z(YB5Fs8^2}zt)7@bu=4f=Q31vDPm*FHnZ0f&-`Wtrw4Vb7*}Kdv-tGqnewOWFs*%$ zy6EOk3O(Hex>haGfySyuC(t}(=RwXHO66yreeaune|K~HD7UMlS#5hNFDYA+f~)d* z+s24Qu&jdl*gc+%s%Y)E2Cn+6%zzBu8HolN&M;km0U_vdx!!RK8FTIFvBk(HO5Ca&=fKTh!CAI`csj{yDe+Xg)S^$)v^3?3 zkn`)4*M0))lpA?VgN{uP^3_%M`&Dt4>|G5MjafE}GGSFvmZr-bR3NM`sIU<-;IP9o zH8OIp@h`#b(e$A%qDdXk?k(mD;hQ?sAEo5%GTu{nIrf>1NbyCKRc+Q>Ve%@^J_cwx zVc{HP8a{Ct#1%iM@vM~xKQs9fZ|x5-d8BvEMs7gXw2Ws`Nx=xYA_s=nU%~ZvV6e`N z*T`vkMS1#xtNV+`M1NcacMsXgk+5EaDLJcSt5QiEw|;_`o|8!qH7|lsFAaA-9brH zAZi5Uz&0Ync&8A%Xt`Ex$e&jB=}Q@YE_th1YoPyK-*+wpHu`f<@@HFNmlGc9WA4Rw z1Hb_;NKrIvSP~bqbN}?c^)lCwYtoYm#nVmHtQ&!Kcx04^;UetS z$st+k4kSZ@V|rPc;WzQF{ZYd*$MXr~#xV9!OpiUFMQ$c5Pwo)Nx~oC{&$?@}Ywia8 zkO{tgcF+1~tByaTlhd3?G|AvHjevhS<kq zt2tsDd$tWxT?hWMp7zSMn1DsU(Dd<Z@dYfvh${Em4@@az(AQ zyfxi{mo5Z82J5ahsjmsLk2;(+sX?p7rMe;L$`CH2SbS3+TF>#XoDjwN+)s9hv>DwL zc!)GX!LfW5`B55LtLfEJqSw`v#^HdRO(|W^0PNjC0|q*)jBcB$lXZ z^$#jE7GzR6!XU);mV_NjByoD`??&)~p2;RFe47Hcp=om;`U%2Ut!@`|2}EI-=4E_p zUy*E_by;!|WEg}qj10R3;k)*B<{0g3$XQzzb<>c3(9hXr$d+ZGnv|W#%pV%}O0R5q z`{(9v1sPi`5ONvvu^L>M{oq%udo!eBFvrs`tnIG;akdN>7k*%pNr6%3ZR^7AxF>Nr zR9Fb*v3%n4v5oO}WZQwJ#!LpA`P9+fYCIKxn|l}xY`WM%lV}wV-;{3X&lqrsjXQi9 ztHdG=yl*NyYV$R4oDhqj(%i|}fjg}5^+K1(WbYk8q>d~{o&~qBeB|UK3yMoyI-Np9 z+iu6m(}I9Cy@tx#2lnL9IQL5}pAl>}zTEe3gJ2rXb&bvR>5gTTAgf>CRyYKv)M!8J zVH|RM-TZK#O>)3@;G9?>{Zf6y>%&0Z1*$HRMw(y;T0@+-SsmV&3HT)w;g1X*R%~0x z^@0iN15z4g?^7*Gmf_(MSwH75JaXafozk(lb-#mVjYRHMNe#Etqn|uL+ueA zdyQ?aTpEYdbkp#XIv;hQB%exOtF%{fVM79DFjor9|SU z;kwdT5-6+%cPDNojG{i64+Hizskert^+V*Qd%ug5!U7UP)2f1*Rm!z!aH5^Rq z`c9p(C4-slecr4GY}e9Lb+DbQYrSoysl~3&lEPQql0vlx;OnoENOB`2s}dUBqg2yl zEKdvEIb_T>K6Lslg;+5dGpqQ7?WeZq_lxpE^49dT_4e?oB)AB4Zz6}IUJ6Fi*i1mb z@QxvY^C@RK;8~WjF_CBiy)vW(0XCJ3A;c{~Aoh)J? z(Nz(Ip-;L%L0)9C*O!KqD=1gVRF*R*%X-SY8(UR?%3he)k-L1-pMmdLPk~v7_-4jw z1~_yXKe=7-x4CCu-?H5=4lEyR*>?=C)xAE1kAfK~G{zMEl zezex?leBA}l!{N~w#=aTc$m2F%>vDbJ|+!XN4_Qzk@!V{zK94?+9tHd+m7l{D({!4 zNs2Gp;*7mEg&q!Pos1|WQ2R#MX7QPqQZb9CE~)Gw?6qo8vE?PaS#Jx83F=g~v^X8g z?i0a2x`vX5d%Do7;h<4)i-WD_@|@@EbhsxvKF_Ax$be04=Qpm;lOtne@8hw?YR(70p=&8%UKlI$zY&W>UK9 z!rblbAxy%2jreB4f{d9qITgCen{acbwqJYB6M4l2+jD@G#y-cls+mM2?;%?wRT?NfEo#xmQ$kA5oW^&dRE@xnv>VZYg zotzegYHpfmBUqM5_1zg-u*;W*H)Y(cRI@T7AL^kQ?2X1+#?Hl3H|uE;wppTnYG!cl zP1YV(rg2VaP{d?bpflb2LoVwXr>TP_b47t;4J`D`dkygO*7MhK+`b)KJsR?QfO zevPucGAgCV)7vzi6jdZeI#~&7|CU=%)>;9bn(ytSA-tv`%)n9qI7fZ{lz}NnZvNU3 zY=2qHzLCa<xT|56{-R;mYNp?v1C4@QyRfchi~3A++`XJ+Kjz6YxMBAU%zU5q*ThtS|j6Z z+~7F-v*NxYLJ$+1$aSM65m*&|zV^G^)kHIBUY=g5e>Yl2$3RV<(^REFjmLrVYByu7 zvIjXn5pAx01^6>Ku9jTw-Wvtt)bJH}K&>m1x@8-y3;9@N;Pr_EFy!nja0Wg`%c1f8`Ab<`LCT<=ob% zI&v=QQFy;?al`QDQG)Xsj7o!#t%aB-r7!%b#UykeYZ2Ri=@U$z8O-<=$3=8w{H?T_ zv$34BGar8Wk{J@3PS3EvDRP6`y*K5s%FhZD{irEPpaJZqX8F^NunviBtpf3zDRoh7h0rp09G$qM*Xy~3wVsbPgjhrikFsj6EC^LoI@6=AsP;x&8+ zxioB@g?Hrg3lo*`P4mH9idS%8k-qNxUv&9Be?0SzBIMW{a%=t76qfR6NE2Jjr(im-15%4BV zz98;Nzs1h0jj40BI;W~a0 zc~-8CrAx*qLa2FNRacfEkmEfL6^|_BOC!dkIYOj>K)_IFNoYTdJrRMo?!mWDj{J$P zmA{-@BhouQ$*7ctD@@F_-$}|y#xtNKF`=G1M3l&1eGnpY`0Bgl_{-;g>!O4E=~T=eh{MniWMi*P4c-6pZz8-x<5bm2od zyqoX@S9E{cgW$9++;6`lJA_obyPnYE1cazZ*wPGvPEzf! zyz-njY^*l(1f4-V8b2oFj?#@*h=JoP*zxrpICC{^sQ9CSMUjRc&RyY8Nkm41Lf}kU z&A;FJWrXg@9GSt3U~absV5YR8lSt$&iwjU8_o6_;2_qb)%15$vxeJ6*%9K@y@u>2y z#3>KW=_#X_Vx-%69|w(`I*9fv$huz!ikL)BGDS$Q)FYPd@`zFL1j9zqCp2$H&5lnzj;wWmp&c?LkT&2g(dvCZenE0Xkz8e)aq@YA{~9`;$|I=LgX7Rp zfKsr$Iy=Ih7_$9AO{PZ?3oy1)iew^0kiL5lzIpW9eV*}MIqwqcFx93PmhC7_Es{}} z#!uhF%u%$vD5m++|Ha10gKIa*%g;dXf?Z3kb~-3v!roTi0@KdYqFIr((4nW)T!W~w z?m=fJV-#*8fhQxn?(=DAF39Kmn3KWHJr=wK`+6U%jl4#|@Uhx-P6;c`4z!3c`THaX zfv?YP=}+0=)kYjIxni=KF48pAlT8R4LKl~R3^Ju`ppC|c9TXf!q6geZP6qz zqS@$w?Z-u=e7r)jnWtT?BlEz8&4C!r!0+DO~25MRt)%# zprM~<-|%p4{5gdf;O7w&1v-}Hy&w2}_A|K3`8bj4m%(;xSh5rqmGD7=fNqQagcc{x zM|~*;5+gWSjKE)N4Wp`WX6Ni1Go=z=hS9~vVl8q=gqe%Q^&|Bn@u)t3?9jp@cK4l4eznTk zN`fDqLesNbe+?FPf{i69%yEge&aztOUBb2;^o?>HcTpf`dHQhN0}``p$tMj<=U@*v z+f4n(w@PFu3yKW0eH;!Y|MPL-!!D4HeVL{O1hQsf0+ z`hw0V@je(2S3_@(cf=L zmq-`dI1Sm1K#m`om~h_w`L@B}))1@#zJ9viTI7(?o>kWy8B%w9V&6}W(dC{(!EOu- z3}L^Y6^Hiy!Y=tqx|V+b@%q;NKL+DUl%R01z< zak|#nAS{r+J!NxYGTY>3t#N!FX-%!xGSbY|^V`vHzZ1eI|DP7lGAydLi^9Xu-7V71 z&?q4~3^{ZREg&ErqBMwfBhnp1=KzAVgoGg7A&qniQj+rxzTdy|_qop5&t7Zod!OXP zxi)6H0E*&j>yyfHnJrU<&ycSs7{1}p($ABsH3BZTevQ9X_ekQkIO`#+| zr+h`Q#iR7W2nQ`bVljfZXdsY>+4}9hH|Z0?PUp^pcl3|w#j+B#BjnD#s~DP$DfYu= z)0Znq=;hQ$Rwdtd9eq=rm2D&_8^~>Y&HKvG-dOMdZ>Td^tM`*dAhavkg?st=nuNwE;{+kDH zC*uH5us{^p4_)9;^f4;&V|q9|QHU(+!EDwTd2x;$omNSVs%jpo4e13}g;<&BY99Vp zlM98)XYLq;+vWM?#CohaT`cSWeC_;M&5>fp*M<(wV8zqcU}wLe-}kBcQ^veicJznxBJ>GvE|cTSx0te zW;X2cnj|t?btaLf#k;+p2+Ap9zj}%<+W7JF_X8oDj8IGWHO1A3u`ge!dPpeBR6M+q z?E4M|JjOk+SO_lT#HEaJ3tdbga3s3Gul#*5 z&@U`370V~*on>KvRAtMOS8c_QKTsF(@)mK|#OdU$IpXZvk)$>iD5q-p0y&e~jh3D4HGSRty! z)O3dq^vY2k*l{}wwSA_Pr>Pwk)YI7I{2E=HXrHa?liEqRm8;iv8tyDDP0MydX;nWw z@=bmmb*06wB2Tp&30ZgNL0AplH4vJd!yR!g4}bvH#A-ih7`_(_A7d05_qYv$HoMVb zm3K5N;H7q`_B>}Sj94<`wWU(SH1EBC$JpuCexDlu!AYM{xy(xI))*Mr^bfQ7R|=?m zz(@b<3UcurnR=s`)z0}?L~^LPm3XgATbBziB_{5tWkU2rr?gK5)A*#jL|?tkW_P;S z+lIzK6IQ025#jLqTV#A#fE1hwDBxn4)Vcgl!zGR88CVD|WcC`OO@)&XK-Q92LqSG+ zAv5_agb3t0gM801y2MjNxZ|>{dbV;)yCnJ8FJlI8M1>qP_)L}7rmJf!8Z?Drf^YL> zQB?QlJYBO`@?if!@zcU^=Lo9ESr?{OF|YDZ=cMF3@A&3^i~C3BPg#?aJ0-vn+L=Pb8G$t6*d zshe!M{UdX3n@%&&3x#)-Y`L9Lp67n@l{YdT{dz@6rp0NLBazE2_XN^qp`%lLO-evY zbDTu-JPu|(NSoUh`)VOyhlYd-is@qYZ1eNl7{0Emy*eN+PcVeRu}9St)MO4)6#qst zC~U$K1T7y^WlBhANyn&#VL%17l?wJ~C|En^`7HRB@}J%nRkJic!j0$c3FJRNZ+Zv^ zKypt|EfT!@FiVMEK~Bc9IYuoUj&(Wx9jHYwX_d6O zM=riJI(oEJ-D>7(;+D)mlV{2efUR0eO*#;pGlS1&60up>sfY@}htz9;l#0Gkz$;6ynlS|}a@rf3 zID#92sWAUK?-*?!SBd zq!n5;bfVS_`i0;vgrG}jb>+eNoL zj{&u7c|)Y>gp}@Y4MCj@yAfau9_J_a{E3&4SPo}^C ziVcI2mhOPDVD&k@c=1Qs)Q7}~kt?S{!Z(aQ(R~7l>Ds+bG!|BjAWmAR`n?x%0vrc7 zlX4b+%ox-0=6*OIa1&t`O76?9U=%iSq9b#>DyMsJtyO;`-DM3mSHjDpwv zr1L?{zWa~pT^-4-u-6&B(^l0FwV5!~xfT~tHvKkn6%NK&4*nIX6wi`T-?kv#F7^*l z0#P_eC?G$@AaDGTzpu|z63(`BYWH;TD}HJQPV4m!E2r59iAi_k=dS%)19^;9DTs$? z)cM=^r66WZ%niaWL)W01Pjo3K4aD(g3)3)g)(HXzh|2Z5C$TikL#XLG*^`?{v>|eW z{Aa*;@>;RhqB@Kp#un>yrzh^&L1I&^X(lJmxgoZ|?Cn#{v9C$^QM7+XyfF#Ziwa&N zQs$%Aa$L-M`wUsHOSc_62^JDJe^Sj5q$TkHPbilm#)z?f+r8cC$?}&)%5WzG178y&I#488 zPyW-}^7)j`pWkgs&~>%U64%MTvf&Zdgr()i!*ebzi)oB9;vxbT>3;sr6D%TAgO(S$ z^91anp=a3WrSt+zD_t?j5g7HdA{yRi$K$DdbE5H2#-}$;u0IC3#)e1s9>OH=Hi~sU zJ2Hkj%@MBi=1TbgT!rHoUQB{6W?Lmm7NXF#-^dFTwbR~wJs>$SEWFTaZK&6M9hDcc zu0HEpU#e)yJx6ov;3kd{(ON+L4hB20BK7n*U;F6R#;%Ta)+Y8PZD5)$0=~PeCofNVZz z$H`ONREiRQ)a$NHufAzfUqg3NNLdZjje3*YTrHF{eu_jJv2`8&%;2UV(U0AJADL{8 zmH{xxUZg6m=`r_F^~h*9tW92I5q-ubmY1T_1%%ZT#k55-9>l~kov zCP&4lGO2Fk@?UxBmA?H5O&Bxv(xfc0&d%`g@!HKdVz)1L@t_UkQI3L^^K6rg(qD$q z1TAmzSrd@G^m`Z4L>3OEuN?QrvLmA30$pbXpMUNF3q7a}Xak56QJ00JgJl%N;eH$t6v9sun@*xJ=#_H4BejeXn<1Ay78m#^ z(hi594CnbnW_*m&vtbXkd({#Wh3en56~s*2SM%t){N%oID--4^mT@xY=fp>04+U=V zOq*dV2vy%V*~8n`DE7g6)@Xz+UpW(+`h%RCPcE# zay!z)Y&BE@;-E1coM$^9eqH5QVWBJ{28gmPn(Z96;Ax2lPuwJhlnvmh&x6c8u)Yk# zfB+Mg-*1&Qo;=Y_5k**1i_mvwMzc*NEsQO5XjK+cem=@Xgy;G?ip=dhbswn|%o}K* ztoEDF+=cY<&wj zAb?hZRW1sZ73&P=0zXJO16KemLbDxUQ&>{DPHWn!q2K7na$w|z2 zdd44@|HZG?&Zrh$-+l>A#4c`5Dx`{sCES!uA{RSBs}ro)tu)q7B)QdVTEu4I9T zmWel=#nrK~_@$N|?Su;bhLVPMKi6d9ITVfW#K%T*dn8ilcAW?d7t49n11Y~rciHk` zF<}AhbW8Ig?%X_d-Rz2BGSW!TK`IiQ7igN#=+46-ywfol*gdzxQeR2ch2q2rt*VID zBh*W_Tdy?8l{tr|{J65P(xwiCGS1&e#yLU+<;82~W{~*$4K>{F}L){f7;qEUU*Q zYF^oQzjDw9)2*H4C`<9zeu@c$4;jRqtG8z89C#dDFOcwL1E}ogIcimH3XR432weK` zcX9aYkIXu0LV-6$(qDN?YBHsW-uEFTu_tnK<1Mj`|mm=_R{_Y4-` zz=zkC8NWDq8BVA4W;o&c>>$NH--rhrYEU}N|LPeB93Y%so#6TNl^WgRqnjavqL== zaH{V{?Egit8tqUZ@_3D|&6s*qdcxa=S<>)SO|@ z990;EF=wtMVp2E33bGIAvd5n{I&mb>TGVD)NjNrC5LoY&FZ=FQldUCjP2Z;!jVcu8 zB{J0caEMFXNYZiP;v^kd9h?Dhqjbf~;a8{fsGVvgg5+m?`EA_em7jU*Sj?5c%Iwkd%VSOBP)sONk6FXoc+|^bVbq&Ti^d!#n75rq974s3&31D#hDrAO(0Z zlBJD|o-Ar--_Tl<(GyF7?Yuh}SclA)d`tblj*C{Ez`0LkQf9`fW2mWY(XyCjTS{{2 zSh7LT5M(;YM0UzPk#Xk@gc2tA`aIlmAU<~srqDc;Hq|gXM_xWf`d>5)_^J8)UNAH& zADE`(;kqm0646u${K(2<2T^5+VmDDHbRu+QxZ1QDq`h-7cuv zvf@eU>@%AS57^L>EPupy#IGvlMcAY}>Buj!ra+;mauc{Z-k~>~A_xWC+eh!?Rr@73 z>ZGE4RwrI}yx>}RrdPywSR=)y8*iDK1l_X;C zc$4SqZk{&*s}lr>G5V=qtJxfrU5k71Uw(SfqZiU8Hx?HsssFPXd1HdyzCO=_w$8Ms z8rs830fp)1a4G$-87ROX_Zc^|x3aT(!^-?q;&7Ps+*&M6|MZu%R(wgRXCv;DAxE#e zAL9rzuaG@?BW+7~-BtG;Mx4vDl=*aOfoXz&L;~IKWO+hji(3_Fu~kFDOr*H{#JfVg zK6wsqGZiK}3oK30{u#`~wf>jIY9`*Qwz!t-5&tUFsZ&YifB~aMz(fB(Q-GnChRt_0 zQL6Id1bOiQlC03*oMx%a#_TgcI%&N93QyLODLT`#kJ_5%y7$jc-C>e(`?Nvrf@ptn zoKr}Fkw6qqTFA7M!C6aU%XF3)9FVBxN@B!p%Q)Gq8DyWO^!xSK*o-0h@u>jr9KH-w zw>e_Kr=4#mfHBGd!=PVjd`FSW`*}1zH z%TRMCDSzX|x6(nRSwRR3+u-^VBg{@TJU!4CYyLL8qZij2wF-K8lR4?#K}+rA2;6W}I$Lc1Hx}>*}@KwK2*wW~lVij`eTk zebyG;r%IapVe>xoUUh{73z>-IunP=q7bX|pOk!hST&xWV32AL}f06pWRRPQ(F4&%v|3)?`55+o>Bi7S9foC zYev=k1kAJEen(`K+dGsyQDFiag>8QnFl)@8xb6*lczD#+_1&E892^`#xi(W%2dCp3 yJ&R6>|I(osM`9Rz+mB$15kWSGUS!`3P#vt?w$ZQVrW!i7>D&GELZb=%;_^T3SCNJQ literal 0 HcmV?d00001 diff --git a/client/src/webpages/dashboard/mrt/manual_review_job/ManualReviewJobContentBlurableVideo.tsx b/client/src/webpages/dashboard/mrt/manual_review_job/ManualReviewJobContentBlurableVideo.tsx index 94107602..e874a597 100644 --- a/client/src/webpages/dashboard/mrt/manual_review_job/ManualReviewJobContentBlurableVideo.tsx +++ b/client/src/webpages/dashboard/mrt/manual_review_job/ManualReviewJobContentBlurableVideo.tsx @@ -1,6 +1,6 @@ import { PlayCircleFilled } from '@ant-design/icons'; import { useEffect, useRef, useState } from 'react'; -import ReactPlayer from 'react-player/lazy'; +import ReactPlayer from 'react-player'; import CoopModal from '../../components/CoopModal'; diff --git a/server/e2e/fixtures/media.ts b/server/e2e/fixtures/media.ts index f4fb0af8..6b1b0295 100644 --- a/server/e2e/fixtures/media.ts +++ b/server/e2e/fixtures/media.ts @@ -1,11 +1,15 @@ /** * Media URLs for e2e tests. The server's item-field validator only accepts - * http(s) URLs (no data URIs), and the audio must actually load to be played, - * so we serve a tiny fixture from the client dev server's `public/` dir + * http(s) URLs (no data URIs), and the audio/video must actually load to be + * played, so we serve tiny fixtures from the client dev server's `public/` dir * (localhost is allowed via ALLOW_USER_INPUT_LOCALHOST_URIS=true). The image * reuses the existing client logo. */ -export const IMAGE_URL = 'http://localhost:3000/logo192.png'; +const mediaBaseUrl = process.env.PLAYWRIGHT_BASE_URL ?? 'http://localhost:3000'; -export const AUDIO_URL = 'http://localhost:3000/e2e/tone-3s.wav'; +export const IMAGE_URL = `${mediaBaseUrl}/logo192.png`; + +export const AUDIO_URL = `${mediaBaseUrl}/e2e/tone-3s.wav`; + +export const VIDEO_URL = `${mediaBaseUrl}/e2e/test-video-2s.mp4`; diff --git a/server/e2e/tests/mrt-job-review.spec.ts b/server/e2e/tests/mrt-job-review.spec.ts index c8a3aa06..1433fbde 100644 --- a/server/e2e/tests/mrt-job-review.spec.ts +++ b/server/e2e/tests/mrt-job-review.spec.ts @@ -2,19 +2,16 @@ import { ScalarTypes, type Field } from '@roostorg/coop-types'; import { uid } from 'uid'; import { expect, jsonStringify, test } from '../fixtures/coop.js'; -import { AUDIO_URL, IMAGE_URL } from '../fixtures/media.js'; +import { AUDIO_URL, IMAGE_URL, VIDEO_URL } from '../fixtures/media.js'; -// VIDEO is intentionally omitted — react-player/lazy (used by -// ManualReviewJobContentBlurableVideo) crashes in vite dev mode ("Element type -// is invalid: lazy element must resolve to a class or function"), taking down -// the whole page (no per-field error boundary). Re-add VIDEO once that is fixed. const FIELDS: Field[] = [ { name: 'text', type: ScalarTypes.STRING, required: true, container: null }, { name: 'image', type: ScalarTypes.IMAGE, required: false, container: null }, { name: 'audio', type: ScalarTypes.AUDIO, required: false, container: null }, + { name: 'video', type: ScalarTypes.VIDEO, required: false, container: null }, ]; -test('an MRT job renders text/image/audio, plays audio, and records a decision', async ({ +test('an MRT job renders text/image/audio/video, plays media, and records a decision', async ({ page, request, deps, @@ -63,6 +60,7 @@ test('an MRT job renders text/image/audio, plays audio, and records a decision', text: uniqueText, image: IMAGE_URL, audio: AUDIO_URL, + video: VIDEO_URL, }); await seed.waitForQueueDrained(); @@ -74,6 +72,7 @@ test('an MRT job renders text/image/audio, plays audio, and records a decision', await expect(page.getByText('Text', { exact: true })).toBeVisible(); await expect(page.getByText('Image', { exact: true })).toBeVisible(); await expect(page.getByText('Audio', { exact: true })).toBeVisible(); + await expect(page.getByText('Video', { exact: true })).toBeVisible(); const audio = page.locator('audio').first(); await expect(audio).toBeVisible(); @@ -83,6 +82,17 @@ test('an MRT job renders text/image/audio, plays audio, and records a decision', await el.play(); }); + const video = page.locator('video').first(); + await expect(video).toBeVisible(); + await video.evaluate(async (el: HTMLVideoElement) => { + // eslint-disable-next-line functional/immutable-data -- DOM elements are mutable by nature. + el.muted = true; + await el.play(); + }); + await expect + .poll(async () => video.evaluate((el: HTMLVideoElement) => el.currentTime)) + .toBeGreaterThan(0); + const submitResponse = page.waitForResponse( (resp) => resp.url().includes('/api/v1/graphql') && From 610bcf08cdaec76b1a980f203c3b796bfcb20053 Mon Sep 17 00:00:00 2001 From: juliet Date: Tue, 14 Jul 2026 22:09:40 -0400 Subject: [PATCH 13/57] Fix for Manual Review Tool: If createdAt datetime is in bad format, store it as null in decision log (#913) * fix(mrt): store null for unparseable item createdAt in decision log Submitting a decision failed with a 500 ("Job submission failed. Please try again.") for any job whose item carries a truthy but unparseable createdAt value. `#logDecision` passed `new Date(itemCreatedAtField)` straight into the `item_created_at` timestamptz column; an unparseable value yields an Invalid Date, which the pg driver serializes to a NaN string that Postgres rejects (22007), failing the whole decision insert. The decision is never recorded and the job is never removed, so the task is stuck in the queue. Normal item submissions can't reach this state because the DATETIME field handler validates dates at intake. A bad value only arrives via a path that skips that validation (e.g. system-generated reports, or a createdAt role mapped to a non-DATETIME field). Normalize at the write boundary: parse the value and store null when it is not a valid date. The column is already nullable, so the decision records and the task clears. Adds a unit regression test for the normalization helper. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01CEU6YHFiBYfTqjgM5Vyt9n * docs(changelog): note the unparseable createdAt decision fix Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01CEU6YHFiBYfTqjgM5Vyt9n * fix(mrt): preserve epoch 0 in parseItemCreatedAt Address review: `!value` treated a numeric 0 (a valid 1970-01-01 epoch) as empty. Guard only null/undefined/empty-string instead, and cover epoch 0 in the test. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01CEU6YHFiBYfTqjgM5Vyt9n * fix(mrt): record unparseable item createdAt values Address review: surface invalid createdAt values instead of silently nulling them. When a present createdAt can't be parsed, emit a tracer span (job id, org id, the raw value) so the bad data is diagnosable and can be backfilled. The decision still saves with a null item_created_at. new Date() returns an Invalid Date rather than throwing, so this detects the invalid parse rather than wrapping in try/catch. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01CEU6YHFiBYfTqjgM5Vyt9n --------- Co-authored-by: Claude Opus 4.8 --- CHANGELOG.md | 4 ++ .../modules/JobDecisioning.test.ts | 40 ++++++++++++++ .../modules/JobDecisioning.ts | 53 +++++++++++++++++-- 3 files changed, 94 insertions(+), 3 deletions(-) create mode 100644 server/services/manualReviewToolService/modules/JobDecisioning.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 69f1c51b..7fa07e87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ **Full Changelog**: https://github.com/roostorg/coop/compare/1.0.2...main +## Review Console + +- Fixed a "Job submission failed" error that prevented reviewers from clearing jobs whose item had an unparseable `Created At` value; the decision now records instead of failing (#913) + # Coop 1.0.2 This release addresses reported security advisories, improves NCMEC CyberTipline reporting, and includes front-end quality-of-life improvements. diff --git a/server/services/manualReviewToolService/modules/JobDecisioning.test.ts b/server/services/manualReviewToolService/modules/JobDecisioning.test.ts new file mode 100644 index 00000000..2efbdf91 --- /dev/null +++ b/server/services/manualReviewToolService/modules/JobDecisioning.test.ts @@ -0,0 +1,40 @@ +import { parseItemCreatedAt } from './JobDecisioning.js'; + +describe('parseItemCreatedAt', () => { + test('parses a valid ISO string', () => { + expect(parseItemCreatedAt('2026-01-01T00:00:00.000Z')).toEqual( + new Date('2026-01-01T00:00:00.000Z'), + ); + }); + + test('parses an epoch-millis number', () => { + expect(parseItemCreatedAt(1735689600000)).toEqual(new Date(1735689600000)); + }); + + test('treats epoch 0 as a valid timestamp, not empty', () => { + expect(parseItemCreatedAt(0)).toEqual(new Date(0)); + }); + + test('passes a Date through', () => { + const d = new Date('2026-01-01T00:00:00.000Z'); + expect(parseItemCreatedAt(d)).toEqual(d); + }); + + test.each([null, undefined, ''])( + 'returns null for empty value %p', + (value) => { + expect(parseItemCreatedAt(value)).toBeNull(); + }, + ); + + // Regression: a truthy-but-unparseable createdAt (seen on reports from + // automated sources) produced an Invalid Date, which throws on pg + // serialization and failed the entire decision insert, surfacing as + // "Job submission failed" in the reviewer UI. + test.each([' ', 'not-a-date', 'garbage', '2026-99-99T99:99:99Z'])( + 'returns null for unparseable value %p instead of an Invalid Date', + (value) => { + expect(parseItemCreatedAt(value)).toBeNull(); + }, + ); +}); diff --git a/server/services/manualReviewToolService/modules/JobDecisioning.ts b/server/services/manualReviewToolService/modules/JobDecisioning.ts index 1be10fec..1081ce70 100644 --- a/server/services/manualReviewToolService/modules/JobDecisioning.ts +++ b/server/services/manualReviewToolService/modules/JobDecisioning.ts @@ -5,12 +5,14 @@ import { type JsonObject } from 'type-fest'; import { type Dependencies } from '../../../iocContainer/index.js'; import { filterNullOrUndefined } from '../../../utils/collections.js'; +import { jsonStringify } from '../../../utils/encoding.js'; import { CoopError, ErrorType, type ErrorInstanceData, } from '../../../utils/errors.js'; import { assertUnreachable } from '../../../utils/misc.js'; +import { isValidDate } from '../../../utils/time.js'; import { isNonEmptyString } from '../../../utils/typescript-types.js'; import { getFieldValueForRole } from '../../itemProcessingService/index.js'; import { type NCMECMediaReport } from '../../ncmecService/ncmecReporting.js'; @@ -75,6 +77,24 @@ type MRTJobAutoCloseReason = */ export const AUTOMATED_DECISION_REVIEWER_ID = ''; +/** + * Normalizes an item's createdAt field value into a Date for the + * `item_created_at` column. A truthy-but-unparseable value (whitespace, or a + * non-ISO format some report sources emit) yields an Invalid Date, which throws + * on pg serialization and fails the entire decision insert. Return null in that + * case so the nullable column absorbs it and the decision still records. + */ +export function parseItemCreatedAt( + value: string | number | Date | null | undefined, +): Date | null { + // Guard only null/undefined/empty-string; a numeric 0 is a valid epoch. + if (value == null || value === '') { + return null; + } + const parsed = new Date(value); + return isValidDate(parsed) ? parsed : null; +} + export type ManualReviewDecisionComponent = | { type: 'IGNORE' } | { @@ -627,9 +647,36 @@ export default class JobDecisioning { job.payload.item.data, ) : null; - const itemCreatedAt = itemCreatedAtField - ? new Date(itemCreatedAtField) - : null; + const itemCreatedAt = parseItemCreatedAt(itemCreatedAtField); + + // Record when a present createdAt couldn't be parsed. We store null so the + // decision still saves, but surface the bad value so it's diagnosable and + // can be backfilled rather than silently dropped. + if ( + itemCreatedAt === null && + itemCreatedAtField != null && + itemCreatedAtField !== '' + ) { + this.tracer.addSpan( + { + resource: 'mrtService', + operation: 'logDecision.invalidItemCreatedAt', + }, + (span) => { + span.setAttribute('job.id', job.id); + span.setAttribute('org.id', orgId); + this.tracer.logSpanFailed( + span, + new Error( + `Unparseable item createdAt for job ${job.id}: ${jsonStringify( + itemCreatedAtField, + )}. Storing null.`, + ), + ); + return null; + }, + ); + } return this.pgQuery .insertInto('manual_review_tool.manual_review_decisions') From e4238cd349663ebdda8b006e66322e9a83d36d46 Mon Sep 17 00:00:00 2001 From: Sunil Yadav Date: Mon, 20 Jul 2026 06:14:13 +0530 Subject: [PATCH 14/57] Make Scylla-backed features (item investigation & user strikes) optional (#918) * Make Scylla-backed features (item investigation, user strikes) optional * Address review: extract shared flag helper, use @ts-expect-error Resolve CodeRabbit nitpicks on PR #918: - Extract the ITEM_INVESTIGATION_AND_STRIKES_ENABLED parsing into a single exported helper (itemInvestigationAndStrikesEnabled) in noOpScylla.ts; the iocContainer factory and the unit test now share it instead of keeping two copies in sync (DRY). - Keep the helper a pure function of its argument (env is read at the call site) so the default-enabled test is independent of the ambient environment. - Replace the 'as unknown as' cast in NoOpScylla.insert() with a scoped @ts-expect-error and justifying comment, per repo guidelines. Co-Authored-By: Rovo Dev * Fixed formatting * ci: re-trigger E2E (investigate investigation.spec flake vs regression) * Gate user strike logic behind ITEM_INVESTIGATION_AND_STRIKES_ENABLED flag When the flag is false, applyUserStrikeFromPublishedActions early-returns to avoid running strike threshold checks against always-zero counts from the NoOpScylla, which would cause escalation actions to fire unexpectedly. --------- Co-authored-by: Rovo Dev Co-authored-by: Juan Mrad --- server/.env.example | 7 ++ server/iocContainer/index.ts | 23 ++++ server/scylla/noOpScylla.test.ts | 78 +++++++++++++ server/scylla/noOpScylla.ts | 104 ++++++++++++++++++ .../userStrikeService/userStrikeService.ts | 5 + 5 files changed, 217 insertions(+) create mode 100644 server/scylla/noOpScylla.test.ts create mode 100644 server/scylla/noOpScylla.ts diff --git a/server/.env.example b/server/.env.example index f91cf6e2..27c6117f 100644 --- a/server/.env.example +++ b/server/.env.example @@ -76,6 +76,13 @@ CLICKHOUSE_PROTOCOL=http HMA_SERVICE_URL=http://localhost:9876 # Scylla Cluster Details +# Set to "false" to run Coop without a Scylla cluster. This disables the two +# Scylla-backed features — Item Investigation (item/user history views) and +# User Strikes (repeat-offender strike counts) — which then no-op: reads return +# empty (strike counts read as 0) and writes are dropped. When "false" (or +# "0"/"no"), the SCYLLA_* connection settings below are not required. +# Defaults to enabled. +ITEM_INVESTIGATION_AND_STRIKES_ENABLED=true SCYLLA_USERNAME=cassandra SCYLLA_PASSWORD=cassandra SCYLLA_HOSTS='127.0.0.1:9042' diff --git a/server/iocContainer/index.ts b/server/iocContainer/index.ts index 91ac142a..91549183 100644 --- a/server/iocContainer/index.ts +++ b/server/iocContainer/index.ts @@ -59,6 +59,9 @@ import makeRuleEvaluator, { type RuleEvaluator, } from '../rule_engine/RuleEvaluator.js'; import { Scylla } from '../scylla/index.js'; +import NoOpScylla, { + itemInvestigationAndStrikesEnabled, +} from '../scylla/noOpScylla.js'; import { makeActionStatisticsService, type ActionStatisticsService, @@ -772,6 +775,9 @@ export default async function getBottle() { executionContext, ); }, + itemInvestigationAndStrikesEnabled( + process.env.ITEM_INVESTIGATION_AND_STRIKES_ENABLED, + ), ), ); @@ -783,6 +789,23 @@ export default async function getBottle() { // keyspace aware and it's very annoying and likely error prone to be // switching keyspaces with `USE KEYSPACE` all the time. bottle.factory('Scylla', () => { + // Scylla backs the item-investigation and user-strike features. Operators + // who don't need those (and don't want to run a Scylla cluster) can set + // `ITEM_INVESTIGATION_AND_STRIKES_ENABLED=false` to swap in a no-op that + // drops writes and returns empty reads, so no `SCYLLA_*` connection env + // vars are required. Defaults to enabled to preserve existing behaviour. + if ( + !itemInvestigationAndStrikesEnabled( + process.env.ITEM_INVESTIGATION_AND_STRIKES_ENABLED, + ) + ) { + // eslint-disable-next-line no-restricted-syntax + logJson( + 'scylla.disabled ITEM_INVESTIGATION_AND_STRIKES_ENABLED=false; using no-op Scylla', + ); + return new NoOpScylla(); + } + const contactPoints = safeGetEnvVar('SCYLLA_HOSTS') .split(',') .map((it) => it.trim()) diff --git a/server/scylla/noOpScylla.test.ts b/server/scylla/noOpScylla.test.ts new file mode 100644 index 00000000..bbf1c404 --- /dev/null +++ b/server/scylla/noOpScylla.test.ts @@ -0,0 +1,78 @@ +import NoOpScylla, { + itemInvestigationAndStrikesEnabled, +} from './noOpScylla.js'; +import Scylla from './scylla.js'; + +/** + * Tests for the Scylla-disabled path used when + * `ITEM_INVESTIGATION_AND_STRIKES_ENABLED=false`. + * + * Two things are covered: + * 1. The behavioural contract of {@link NoOpScylla} (drops writes, empty reads, + * connect/close resolve). + * 2. The exact flag-parsing predicate (`itemInvestigationAndStrikesEnabled`) + * used by the `Scylla` DI factory in `iocContainer` to decide + * enabled-vs-disabled. Imported directly (not mirrored) so the + * default-enabled (upstream-preserving) behaviour is guarded by a test. + */ + +describe('ITEM_INVESTIGATION_AND_STRIKES_ENABLED gate predicate', () => { + test('defaults to enabled when unset (preserves upstream behaviour)', () => { + expect(itemInvestigationAndStrikesEnabled(undefined)).toBe(true); + expect(itemInvestigationAndStrikesEnabled('')).toBe(true); + }); + + test('is disabled only for explicit falsey values', () => { + for (const v of ['false', 'FALSE', ' false ', '0', 'no', 'No']) { + expect(itemInvestigationAndStrikesEnabled(v)).toBe(false); + } + }); + + test('stays enabled for truthy / unrelated values', () => { + for (const v of ['true', 'TRUE', '1', 'yes', 'anything']) { + expect(itemInvestigationAndStrikesEnabled(v)).toBe(true); + } + }); +}); + +describe('NoOpScylla', () => { + // A minimal DB shape for the generic parameter. + type TestDB = { widgets: { id: number; name: string } }; + const noop = new NoOpScylla(); + + test('is a Scylla so it satisfies every consumer unchanged', () => { + expect(noop).toBeInstanceOf(Scylla); + }); + + test('connect() and close() resolve (eager callers proceed)', async () => { + await expect(noop.connect()).resolves.toBeUndefined(); + await expect(noop.close()).resolves.toBeUndefined(); + }); + + test('insert() resolves and drops the write', async () => { + await expect( + noop.insert({ into: 'widgets', row: { id: 1, name: 'a' } }), + ).resolves.toBeDefined(); + }); + + test('select() returns an empty result set', async () => { + await expect( + noop.select({ from: 'widgets', select: '*' }), + ).resolves.toEqual([]); + }); + + test('selectStream() yields nothing', async () => { + const rows = await (async () => { + const collected = []; + for await (const row of noop.selectStream({ + from: 'widgets', + select: '*', + })) { + // eslint-disable-next-line functional/immutable-data + collected.push(row); + } + return collected; + })(); + expect(rows).toEqual([]); + }); +}); diff --git a/server/scylla/noOpScylla.ts b/server/scylla/noOpScylla.ts new file mode 100644 index 00000000..99ab71fa --- /dev/null +++ b/server/scylla/noOpScylla.ts @@ -0,0 +1,104 @@ +import { type CqlSelectOptions, type DBDefinition } from './cqlUtils.js'; +import Scylla from './scylla.js'; + +/** + * Parses the `ITEM_INVESTIGATION_AND_STRIKES_ENABLED` feature flag from its raw + * string value (i.e. `process.env.ITEM_INVESTIGATION_AND_STRIKES_ENABLED`). + * + * Shared by the `Scylla` DI factory in `iocContainer` (to decide whether to + * return a real Scylla or a {@link NoOpScylla}) and by the unit tests. Defaults + * to enabled when unset/empty so existing deployments are unaffected; only the + * explicit falsey values `false`/`0`/`no` (case/whitespace-insensitive) disable + * the Scylla-backed features. + * + * Kept as a pure function of its argument (it does not read `process.env` + * itself) so callers own where the value comes from and tests stay independent + * of the ambient environment. + */ +export function itemInvestigationAndStrikesEnabled( + raw: string | undefined, +): boolean { + return !['false', '0', 'no'].includes((raw ?? 'true').trim().toLowerCase()); +} + +/** + * A no-op implementation of {@link Scylla} used when the Scylla-backed features + * (item investigation and user strikes) are disabled via + * `ITEM_INVESTIGATION_AND_STRIKES_ENABLED=false`. + * + * Scylla has no managed offering on some deployment platforms, and some + * operators do not need the features that depend on it. Rather than gate the + * ~100+ call sites that touch Scylla, we gate at the single dependency-injection + * chokepoint (the `Scylla` factory in `iocContainer`) and return this no-op. + * + * Behaviour when disabled: + * - `connect()` / `close()` resolve immediately (so the item-processing worker's + * eager `await scylla.connect()` succeeds without a real cluster). + * - `insert()` resolves and drops the write. + * - `select()` returns an empty result set. + * - `selectStream()` yields nothing. + * + * This keeps every consumer compiling and running unchanged; they simply observe + * empty data (e.g. user strike counts read as 0) and their writes are discarded. + */ +export default class NoOpScylla extends Scylla { + constructor() { + // The base class only stores the client and never touches it once all + // query methods are overridden below, so a null client cast is safe here. + // eslint-disable-next-line @typescript-eslint/no-explicit-any + super(null as any); + } + + /** No cluster to connect to; resolve so eager callers proceed. */ + async connect(): Promise { + return undefined; + } + + /** Nothing to shut down. */ + async close(): Promise { + return undefined; + } + + /** Drop the write. */ + override async insert( + _opts: { + [K in RelationName]: { + into: RelationName; + row: DB[K]; + ttlInSeconds?: number; + }; + }[RelationName], + ): Promise['insert']>>> { + // There is no cluster to write to; return a minimal empty result set. The + // real driver returns a full `ResultSet`, but no-op consumers never read + // the result, so a minimal shape is sufficient here. + // @ts-expect-error - minimal stand-in for the driver's ResultSet; unused by callers + return { rows: [], rowLength: 0 }; + } + + /** Return no rows. */ + override async select< + RelationName extends keyof DB & string, + Cols extends keyof DB[RelationName] & string = keyof DB[RelationName] & + string, + >( + _opts: CqlSelectOptions, + ): Promise<{ [K in Cols]: DB[RelationName][K] }[]> { + return []; + } + + /** Yield nothing. */ + override selectStream< + RelationName extends keyof DB & string, + Cols extends keyof DB[RelationName] & string = keyof DB[RelationName] & + string, + >( + _opts: CqlSelectOptions, + ): AsyncIterableIterator<{ [K in Cols]: DB[RelationName][K] }> { + type Selection = { [K in Cols]: DB[RelationName][K] }; + async function* empty(): AsyncIterableIterator { + // Intentionally yields nothing. + } + return empty(); + } +} diff --git a/server/services/userStrikeService/userStrikeService.ts b/server/services/userStrikeService/userStrikeService.ts index 5307abd8..49e687e7 100644 --- a/server/services/userStrikeService/userStrikeService.ts +++ b/server/services/userStrikeService/userStrikeService.ts @@ -33,6 +33,7 @@ export class UserStrikeService { private readonly getUserStrikeTTLinDays: Dependencies['getUserStrikeTTLInDaysEventuallyConsistent'], private readonly actionExecutionsAdapter: IActionExecutionsAdapter, private readonly publishActions: Dependencies['ActionPublisher']['publishActions'], + private readonly enabled: boolean = true, ) { this.scylla = scylla; this.moderationConfigService = moderationConfigService; @@ -97,6 +98,10 @@ export class UserStrikeService { actorEmail?: string; }, ) { + if (!this.enabled) { + return; + } + const targetUser = getUserFromActionTargetItem(executionContext.targetItem); const mostSeverePolicy = this.findMostSeverePolicyViolationFromActions(triggeredActions); From dd3f504c28f2bb5b70f66529ca5b30b31158666f Mon Sep 17 00:00:00 2001 From: Juan Mrad Date: Sun, 19 Jul 2026 20:04:43 -0500 Subject: [PATCH 15/57] Scope zizmor workflow to only run when workflow files change (#924) Add paths filter so the GitHub Actions security scan only triggers when .github/workflows/ files are modified, instead of on every PR. --- .github/workflows/zizmor.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml index 792fc9b8..13ba49c3 100644 --- a/.github/workflows/zizmor.yml +++ b/.github/workflows/zizmor.yml @@ -3,8 +3,10 @@ name: GitHub Actions security analysis on: push: branches: ['main'] + paths: ['.github/workflows/**'] pull_request: branches: ['**'] + paths: ['.github/workflows/**'] concurrency: group: zizmor-${{ github.ref }} From 1b2fb8c895572b15615e59de71c5b4632afde881 Mon Sep 17 00:00:00 2001 From: Dom Rettig Date: Sun, 19 Jul 2026 18:10:46 -0700 Subject: [PATCH 16/57] Fix Oldest Task Age showing the newest job's age instead of the oldest (#909) getOldestJobCreatedAtForExistingQueue fetched the first waiting/delayed job with queue.getJobs([state], 0, 0), but BullMQ's getJobs defaults to descending order, so index 0 is the most recently added job. The MRT queues dashboard's Oldest Task Age column therefore showed the newest task's age. Switch to BullMQ's getWaiting/getDelayed getters, which return jobs oldest-first. Adds a regression test that enqueues an older job then a newer one and asserts the older createdAt is returned; it fails against the previous implementation. The dummy-job payload builder it needs was previously copied per test file, so it is extracted into a shared test/fixtureHelpers/makeDummyMrtJobPayload.ts fixture (using instantiateOpaqueType instead of eslint-disabled type assertions). Co-authored-by: Claude Fable 5 Co-authored-by: Juan Mrad --- CHANGELOG.md | 1 + .../manualReviewToolService.test.ts | 51 +++++-------------- .../modules/QueueOperations.test.ts | 39 ++++++++++++++ .../modules/QueueOperations.ts | 12 +++-- .../fixtureHelpers/makeDummyMrtJobPayload.ts | 41 +++++++++++++++ 5 files changed, 100 insertions(+), 44 deletions(-) create mode 100644 server/test/fixtureHelpers/makeDummyMrtJobPayload.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 7fa07e87..bc053aef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ## Review Console - Fixed a "Job submission failed" error that prevented reviewers from clearing jobs whose item had an unparseable `Created At` value; the decision now records instead of failing (#913) +- Fixed "Oldest Task Age" on the MRT queues dashboard showing the newest job's age instead of the oldest (#909) # Coop 1.0.2 diff --git a/server/services/manualReviewToolService/manualReviewToolService.test.ts b/server/services/manualReviewToolService/manualReviewToolService.test.ts index 8c100b1a..289b6bdf 100644 --- a/server/services/manualReviewToolService/manualReviewToolService.test.ts +++ b/server/services/manualReviewToolService/manualReviewToolService.test.ts @@ -5,6 +5,7 @@ import { v1 as uuidv1 } from 'uuid'; import createMrtQueue from '../../test/fixtureHelpers/createMrtQueue.js'; import createOrg from '../../test/fixtureHelpers/createOrg.js'; import createUser from '../../test/fixtureHelpers/createUser.js'; +import makeDummyMrtJobPayload from '../../test/fixtureHelpers/makeDummyMrtJobPayload.js'; import { makeTransactionalTestWithFixture } from '../../test/harness/transactionalTest.js'; import { type MockedServer } from '../../test/setupMockedServer.js'; import { instantiateOpaqueType } from '../../utils/typescript-types.js'; @@ -23,34 +24,6 @@ import { jobIdToGuid } from './modules/QueueOperations.js'; type TestDeps = MockedServer['deps']; -function makeDummyJob() { - return { - createdAt: new Date(), - policyIds: [] as string[], - payload: { - kind: 'DEFAULT', - reportHistory: [] as ReportHistory, - item: instantiateOpaqueType({ - submissionId: makeSubmissionId(), - submissionTime: new Date(), - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - data: {} as NormalizedItemData, - itemTypeIdentifier: { - id: uuidv1(), - version: new Date().toISOString(), - schemaVariant: 'original', - }, - creator: { - id: uuidv1(), - typeId: uuidv1(), - }, - itemId: uuidv1(), - }), - enqueueSourceInfo: { kind: 'REPORT' }, - }, - } as const; -} - function makeDummyNcmecJob() { return { createdAt: new Date(), @@ -270,7 +243,7 @@ describe('Manual Review Tool Service', () => { queueId = queue.id, reviewerId = uuidv1(), reviewerEmail = 'test@test.com', - jobPayload = makeDummyJob(); + jobPayload = makeDummyMrtJobPayload(); const itemId = jobPayload.payload.item.itemId, itemTypeId = jobPayload.payload.item.itemTypeIdentifier.id; @@ -351,7 +324,7 @@ describe('Manual Review Tool Service', () => { async ({ mrtService, org, queue }) => { const orgId = org.id, queueId = queue.id, - jobPayload = makeDummyJob(); + jobPayload = makeDummyMrtJobPayload(); await mrtService['queueOps']['addJob']({ jobPayload, @@ -415,7 +388,7 @@ describe('Manual Review Tool Service', () => { const reviewerId = uuidv1(); const reviewerEmail = 'test@test.com'; - const jobPayload = makeDummyJob(); + const jobPayload = makeDummyMrtJobPayload(); const itemId = jobPayload.payload.item.itemId; const itemTypeId = jobPayload.payload.item.itemTypeIdentifier.id; @@ -470,7 +443,7 @@ describe('Manual Review Tool Service', () => { const reviewerId = uuidv1(); const reviewerEmail = 'test@test.com'; - const jobPayload = makeDummyJob(); + const jobPayload = makeDummyMrtJobPayload(); const itemId = jobPayload.payload.item.itemId; const itemTypeId = jobPayload.payload.item.itemTypeIdentifier.id; @@ -525,7 +498,7 @@ describe('Manual Review Tool Service', () => { const reviewerId = uuidv1(); const reviewerEmail = 'test@test.com'; - const jobPayload = makeDummyJob(); + const jobPayload = makeDummyMrtJobPayload(); const itemId = jobPayload.payload.item.itemId; const itemTypeId = jobPayload.payload.item.itemTypeIdentifier.id; @@ -581,7 +554,7 @@ describe('Manual Review Tool Service', () => { const reviewerId = uuidv1(); const reviewerEmail = 'test@test.com'; - const jobPayload = makeDummyJob(); + const jobPayload = makeDummyMrtJobPayload(); await mrtService['queueOps']['addJob']({ jobPayload, @@ -629,7 +602,7 @@ describe('Manual Review Tool Service', () => { const reviewerId = uuidv1(); const reviewerEmail = 'test@test.com'; - const jobPayload = makeDummyJob(); + const jobPayload = makeDummyMrtJobPayload(); await mrtService['queueOps']['addJob']({ jobPayload, @@ -675,7 +648,7 @@ describe('Manual Review Tool Service', () => { const reviewerId = uuidv1(); const reviewerEmail = 'test@test.com'; - const jobPayload = makeDummyJob(); + const jobPayload = makeDummyMrtJobPayload(); const itemId = jobPayload.payload.item.itemId; const itemTypeId = jobPayload.payload.item.itemTypeIdentifier.id; @@ -784,7 +757,7 @@ describe('Manual Review Tool Service', () => { const reviewerId = uuidv1(); const reviewerEmail = 'test@test.com'; - const jobPayload = makeDummyJob(); + const jobPayload = makeDummyMrtJobPayload(); const itemId = jobPayload.payload.item.itemId; const itemTypeId = jobPayload.payload.item.itemTypeIdentifier.id; @@ -843,7 +816,7 @@ describe('Manual Review Tool Service', () => { const reviewerId = uuidv1(); const reviewerEmail = 'test@test.com'; - const jobPayload = makeDummyJob(); + const jobPayload = makeDummyMrtJobPayload(); const itemId = jobPayload.payload.item.itemId; const itemTypeId = jobPayload.payload.item.itemTypeIdentifier.id; @@ -898,7 +871,7 @@ describe('Manual Review Tool Service', () => { const reviewerId = uuidv1(); const reviewerEmail = 'test@test.com'; - const jobPayload = makeDummyJob(); + const jobPayload = makeDummyMrtJobPayload(); const itemId = jobPayload.payload.item.itemId; const itemTypeId = jobPayload.payload.item.itemTypeIdentifier.id; diff --git a/server/services/manualReviewToolService/modules/QueueOperations.test.ts b/server/services/manualReviewToolService/modules/QueueOperations.test.ts index 182d4edc..915c9672 100644 --- a/server/services/manualReviewToolService/modules/QueueOperations.test.ts +++ b/server/services/manualReviewToolService/modules/QueueOperations.test.ts @@ -6,6 +6,7 @@ import createContentItemTypes from '../../../test/fixtureHelpers/createContentIt import createMrtQueue from '../../../test/fixtureHelpers/createMrtQueue.js'; import createOrg from '../../../test/fixtureHelpers/createOrg.js'; import createUser from '../../../test/fixtureHelpers/createUser.js'; +import makeDummyMrtJobPayload from '../../../test/fixtureHelpers/makeDummyMrtJobPayload.js'; import { makeTransactionalTestWithFixture } from '../../../test/harness/transactionalTest.js'; import { UserPermission } from '../../userManagementService/index.js'; import { @@ -203,6 +204,44 @@ describe('QueueOperations', () => { }, ); + // Regression: this used to read the queue with getJobs([state], 0, 0), + // which defaults to descending order and returns the *newest* job — the + // dashboard's "Oldest Task Age" column showed the newest task's age. + testWithQueueAndActions()( + 'getOldestJobCreatedAt returns the oldest waiting job, not the newest', + async ({ org, queue, mrtService }) => { + const olderCreatedAt = new Date(Date.now() - 2 * 60 * 60 * 1000); + const newerCreatedAt = new Date(Date.now() - 60 * 1000); + + // Enqueue the older job first: BullMQ pushes new jobs onto the head of + // the wait list, so a descending read returns the most recently added + // job and this test fails without the oldest-first fix. + await mrtService['queueOps']['addJob']({ + orgId: org.id, + queueId: queue.id, + enqueueSourceInfo: { kind: 'REPORT' }, + jobPayload: makeDummyMrtJobPayload({ createdAt: olderCreatedAt }), + }); + await mrtService['queueOps']['addJob']({ + orgId: org.id, + queueId: queue.id, + enqueueSourceInfo: { kind: 'REPORT' }, + jobPayload: makeDummyMrtJobPayload({ createdAt: newerCreatedAt }), + }); + + const oldest = await mrtService.getOldestJobCreatedAt({ + orgId: org.id, + queueId: queue.id, + isAppealsQueue: false, + }); + + expect(oldest).not.toBeNull(); + // BullMQ round-trips job data through JSON, so createdAt may come back + // as an ISO string; compare by timestamp. + expect(new Date(oldest!).getTime()).toEqual(olderCreatedAt.getTime()); + }, + ); + testWithQueueAndActions()( 'deleteAllJobsFromQueue accepts MANAGE_ORG', async ({ org, queue, mrtService }) => { diff --git a/server/services/manualReviewToolService/modules/QueueOperations.ts b/server/services/manualReviewToolService/modules/QueueOperations.ts index b92c654d..d4afc212 100644 --- a/server/services/manualReviewToolService/modules/QueueOperations.ts +++ b/server/services/manualReviewToolService/modules/QueueOperations.ts @@ -1589,12 +1589,14 @@ export default class QueueOperations { ? await this.#getBullAppealQueue(orgId, queueId) : await this.#getBullQueue(orgId, queueId); - // Get the first waiting job and first delayed job - // BullMQ maintains FIFO order within each state, so we only need to compare - // the first job from each state to find the oldest overall + // Get the first waiting job and first delayed job. getWaiting/getDelayed + // return jobs oldest-first, so we only need to compare the first job from + // each state to find the oldest overall. NB: the equivalent + // queue.getJobs([state], 0, 0) defaults to descending order and would + // return the *newest* job instead. const [waitingJobs, delayedJobs] = await Promise.all([ - queue.getJobs(['waiting'], 0, 0), - queue.getJobs(['delayed'], 0, 0), + queue.getWaiting(0, 0), + queue.getDelayed(0, 0), ]); // If no jobs exist in either state, return null diff --git a/server/test/fixtureHelpers/makeDummyMrtJobPayload.ts b/server/test/fixtureHelpers/makeDummyMrtJobPayload.ts new file mode 100644 index 00000000..d4853a38 --- /dev/null +++ b/server/test/fixtureHelpers/makeDummyMrtJobPayload.ts @@ -0,0 +1,41 @@ +import { v1 as uuidv1 } from 'uuid'; + +import { + makeSubmissionId, + type ItemSubmissionWithTypeIdentifier, + type NormalizedItemData, +} from '../../services/itemProcessingService/index.js'; +import { type ReportHistory } from '../../services/manualReviewToolService/index.js'; +import { instantiateOpaqueType } from '../../utils/typescript-types.js'; + +/** + * Builds a minimal DEFAULT-kind manual review job payload suitable for + * QueueOperations.addJob in tests. Every id is freshly generated, so each + * call produces a distinct item (and therefore a distinct Bull job). + */ +export default function makeDummyMrtJobPayload(opts?: { createdAt?: Date }) { + return { + createdAt: opts?.createdAt ?? new Date(), + policyIds: [] as string[], + payload: { + kind: 'DEFAULT', + reportHistory: [] as ReportHistory, + item: instantiateOpaqueType({ + submissionId: makeSubmissionId(), + submissionTime: new Date(), + data: instantiateOpaqueType({}), + itemTypeIdentifier: { + id: uuidv1(), + version: new Date().toISOString(), + schemaVariant: 'original', + }, + creator: { + id: uuidv1(), + typeId: uuidv1(), + }, + itemId: uuidv1(), + }), + enqueueSourceInfo: { kind: 'REPORT' }, + }, + } as const; +} From af4a8387883f76a33dcd5554a2744c9a9e114541 Mon Sep 17 00:00:00 2001 From: juliet Date: Mon, 20 Jul 2026 20:00:09 -0400 Subject: [PATCH 17/57] Fix "Something Went Wrong" when opening MRT jobs with an unparseable Created At (#916) * fix(mrt): guard client date rendering against unparseable createdAt Unguarded date-fns format/formatDistanceToNow calls threw RangeError: Invalid time value on an unparseable createdAt, unmounting the task detail and queue preview into the "Something Went Wrong" error boundary. Add safeFormat/safeFormatDistanceToNow helpers that fall back to "Unknown" for invalid dates, and use them on the MRT render path. Client-side counterpart to the server-side decision-log fix in #913. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01AKvvCLkcC7LM1znjzvDjDA * add guard on more functions to prevent errors on invalid timezone formats. --------- Co-authored-by: Claude Opus 4.8 Co-authored-by: Juan Mrad --- CHANGELOG.md | 1 + client/src/utils/time.test.ts | 106 ++++++++++++++++++ client/src/utils/time.ts | 56 ++++++++- .../mrt/ManualReviewQueueJobsPreview.tsx | 4 +- .../manual_review_job/ReportInfoComponent.tsx | 4 +- .../v2/ManualReviewJobCommentSection.tsx | 6 +- 6 files changed, 163 insertions(+), 14 deletions(-) create mode 100644 client/src/utils/time.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index bc053aef..eff07991 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ## Review Console - Fixed a "Job submission failed" error that prevented reviewers from clearing jobs whose item had an unparseable `Created At` value; the decision now records instead of failing (#913) +- Fixed a "Something Went Wrong" page when opening a job or queue whose `Created At` value was unparseable; the affected date now shows `Unknown` instead of blanking the view (#916) - Fixed "Oldest Task Age" on the MRT queues dashboard showing the newest job's age instead of the oldest (#909) # Coop 1.0.2 diff --git a/client/src/utils/time.test.ts b/client/src/utils/time.test.ts new file mode 100644 index 00000000..dc58b233 --- /dev/null +++ b/client/src/utils/time.test.ts @@ -0,0 +1,106 @@ +import { + parseDatetimeToMonthDayYearDateStringInCurrentTimeZone, + parseDatetimeToReadableStringInCurrentTimeZone, + parseDatetimeToReadableStringInUTC, + safeFormat, + safeFormatDistanceToNow, +} from './time'; + +describe('Time utils tests', () => { + describe('safeFormat', () => { + it('formats a valid date', () => { + expect(safeFormat('2026-07-15T13:06:00Z', 'yyyy-MM-dd')).toBe( + '2026-07-15', + ); + }); + + it('formats the epoch instead of treating it as empty', () => { + expect(safeFormat(new Date(0), 'yyyy')).not.toBe('Unknown'); + }); + + it('returns the fallback for an unparseable string without throwing', () => { + expect(safeFormat('not a date', 'yyyy-MM-dd')).toBe('Unknown'); + }); + + it('returns the fallback for null and undefined', () => { + expect(safeFormat(null, 'yyyy-MM-dd')).toBe('Unknown'); + expect(safeFormat(undefined, 'yyyy-MM-dd')).toBe('Unknown'); + }); + + it('honors a custom fallback', () => { + expect(safeFormat('not a date', 'yyyy-MM-dd', '-')).toBe('-'); + }); + }); + + describe('safeFormatDistanceToNow', () => { + it('returns a relative string for a valid date', () => { + expect(safeFormatDistanceToNow(new Date())).toContain('ago'); + }); + + it('returns the fallback for an unparseable string without throwing', () => { + expect(safeFormatDistanceToNow('not a date')).toBe('Unknown'); + }); + + it('returns the fallback for null', () => { + expect(safeFormatDistanceToNow(null)).toBe('Unknown'); + }); + }); + + describe('parseDatetimeToReadableStringInUTC', () => { + it('formats a valid date', () => { + expect( + parseDatetimeToReadableStringInUTC('2026-07-15T13:06:00Z'), + ).toMatch(/07\/15\/26/); + }); + + it('returns fallback for an unparseable string', () => { + expect(parseDatetimeToReadableStringInUTC('not a date')).toBe('Unknown'); + }); + + it('returns fallback for null', () => { + expect(parseDatetimeToReadableStringInUTC(null)).toBe('Unknown'); + }); + }); + + describe('parseDatetimeToReadableStringInCurrentTimeZone', () => { + it('formats a valid date', () => { + expect( + parseDatetimeToReadableStringInCurrentTimeZone('2026-07-15T13:06:00Z'), + ).toMatch(/07\/15\/26/); + }); + + it('returns fallback for an unparseable string', () => { + expect(parseDatetimeToReadableStringInCurrentTimeZone('not a date')).toBe( + 'Unknown', + ); + }); + + it('returns fallback for null', () => { + expect(parseDatetimeToReadableStringInCurrentTimeZone(null)).toBe( + 'Unknown', + ); + }); + }); + + describe('parseDatetimeToMonthDayYearDateStringInCurrentTimeZone', () => { + it('formats a valid date', () => { + expect( + parseDatetimeToMonthDayYearDateStringInCurrentTimeZone( + '2026-07-15T13:06:00Z', + ), + ).toMatch(/Jul 15, 2026/); + }); + + it('returns fallback for an unparseable string', () => { + expect( + parseDatetimeToMonthDayYearDateStringInCurrentTimeZone('not a date'), + ).toBe('Unknown'); + }); + + it('returns fallback for null', () => { + expect(parseDatetimeToMonthDayYearDateStringInCurrentTimeZone(null)).toBe( + 'Unknown', + ); + }); + }); +}); diff --git a/client/src/utils/time.ts b/client/src/utils/time.ts index 8be15161..74f55ed7 100644 --- a/client/src/utils/time.ts +++ b/client/src/utils/time.ts @@ -1,5 +1,12 @@ import { DateString } from '@roostorg/coop-types'; -import { addDays, addHours, format, isBefore } from 'date-fns'; +import { + addDays, + addHours, + format, + formatDistanceToNow, + isBefore, + isValid, +} from 'date-fns'; export enum LookbackLength { CUSTOM = 'Custom', @@ -32,10 +39,41 @@ function toDate(date: string | DateString | Date): Date { return date instanceof Date ? date : new Date(date); } +type MaybeDate = string | DateString | Date | null | undefined; + +/** + * date-fns `format` throws on an invalid date; this returns `fallback` instead. + */ +export function safeFormat( + date: MaybeDate, + formatStr: string, + fallback = 'Unknown', +): string { + if (date == null) return fallback; + const d = toDate(date); + return isValid(d) ? format(d, formatStr) : fallback; +} + +/** + * date-fns `formatDistanceToNow` throws on an invalid date; this returns + * `fallback` instead. + */ +export function safeFormatDistanceToNow( + date: MaybeDate, + fallback = 'Unknown', +): string { + if (date == null) return fallback; + const d = toDate(date); + return isValid(d) ? formatDistanceToNow(d, { addSuffix: true }) : fallback; +} + export function parseDatetimeToReadableStringInUTC( - date: string | DateString | Date, + date: MaybeDate, + fallback = 'Unknown', ): string { + if (date == null) return fallback; const d = toDate(date); + if (!isValid(d)) return fallback; return new Intl.DateTimeFormat('en-US', { timeZone: 'UTC', month: '2-digit', @@ -51,15 +89,21 @@ export function parseDatetimeToReadableStringInUTC( } export function parseDatetimeToReadableStringInCurrentTimeZone( - date: string | DateString | Date, + date: MaybeDate, + fallback = 'Unknown', ): string { - return format(toDate(date), 'MM/dd/yy hh:mm:ss a'); + if (date == null) return fallback; + const d = toDate(date); + return isValid(d) ? format(d, 'MM/dd/yy hh:mm:ss a') : fallback; } export function parseDatetimeToMonthDayYearDateStringInCurrentTimeZone( - date: string | DateString | Date, + date: MaybeDate, + fallback = 'Unknown', ): string { - return format(toDate(date), 'MMM d, yyyy'); + if (date == null) return fallback; + const d = toDate(date); + return isValid(d) ? format(d, 'MMM d, yyyy') : fallback; } export function startOfHourUTC(date: Date): Date { diff --git a/client/src/webpages/dashboard/mrt/ManualReviewQueueJobsPreview.tsx b/client/src/webpages/dashboard/mrt/ManualReviewQueueJobsPreview.tsx index 3e25ae0e..0e96d44d 100644 --- a/client/src/webpages/dashboard/mrt/ManualReviewQueueJobsPreview.tsx +++ b/client/src/webpages/dashboard/mrt/ManualReviewQueueJobsPreview.tsx @@ -1,5 +1,5 @@ +import { safeFormat } from '@/utils/time'; import { gql } from '@apollo/client'; -import { format } from 'date-fns'; import { useMemo } from 'react'; import { useParams } from 'react-router-dom'; import { Row } from 'react-table'; @@ -198,7 +198,7 @@ export default function ManualReviewQueueJobsPreview() {
), createdAt: ( -
{format(new Date(values.createdAt), 'MM/dd/yy hh:mm a')}
+
{safeFormat(values.createdAt, 'MM/dd/yy hh:mm a')}
), jobId: values.jobId, values, diff --git a/client/src/webpages/dashboard/mrt/manual_review_job/ReportInfoComponent.tsx b/client/src/webpages/dashboard/mrt/manual_review_job/ReportInfoComponent.tsx index 8dd2187c..8de6fabc 100644 --- a/client/src/webpages/dashboard/mrt/manual_review_job/ReportInfoComponent.tsx +++ b/client/src/webpages/dashboard/mrt/manual_review_job/ReportInfoComponent.tsx @@ -13,7 +13,7 @@ import { userHasPermissions } from '@/routing/permissions'; import { filterNullOrUndefined } from '@/utils/collections'; import { getFieldValueForRole } from '@/utils/itemUtils'; import { selectPreferredUserItem } from '@/utils/manualReviewTool'; -import { format } from 'date-fns'; +import { safeFormat } from '@/utils/time'; import { ExternalLink } from 'lucide-react'; import { useCallback } from 'react'; import { Link } from 'react-router-dom'; @@ -153,7 +153,7 @@ export default function ReportInfoComponent(props: { {isAppeal ? 'Appeal ' : 'Report '}Received - {format(new Date(createdAt as string), 'MM/dd/yy hh:mm a')} + {safeFormat(createdAt, 'MM/dd/yy hh:mm a')} diff --git a/client/src/webpages/dashboard/mrt/manual_review_job/v2/ManualReviewJobCommentSection.tsx b/client/src/webpages/dashboard/mrt/manual_review_job/v2/ManualReviewJobCommentSection.tsx index 1a5fc477..47417ab9 100644 --- a/client/src/webpages/dashboard/mrt/manual_review_job/v2/ManualReviewJobCommentSection.tsx +++ b/client/src/webpages/dashboard/mrt/manual_review_job/v2/ManualReviewJobCommentSection.tsx @@ -1,3 +1,4 @@ +import { safeFormatDistanceToNow } from '@/utils/time'; import { CommentOutlined, DeleteOutlined, @@ -7,7 +8,6 @@ import { } from '@ant-design/icons'; import { gql } from '@apollo/client'; import { Button, Input } from 'antd'; -import { formatDistanceToNow } from 'date-fns'; import { useEffect, useRef, useState } from 'react'; import ComponentLoading from '../../../../../components/common/ComponentLoading'; @@ -99,9 +99,7 @@ function ManualReviewJobComment(props: { isBeingDeleted ? 'text-gray-300' : 'text-gray-500' }`} > - {formatDistanceToNow(new Date(comment.createdAt as string), { - addSuffix: true, - })} + {safeFormatDistanceToNow(comment.createdAt)}
Date: Mon, 27 Jul 2026 12:31:25 +0100 Subject: [PATCH 18/57] refactor: remove hard-coded NCMEC test-org allowlist (#930) The testOrgs allowlist (['4def6a77d6a','acc701627cb']) hard-coded two org IDs that were silently suppressed with PERMANENT_ERROR before any network call. Hard-coded org IDs in source don't belong here; the NCMEC_ENV-based sandbox/production routing already handles test vs. prod submissions. Co-Authored-By: pi --- .../services/ncmecService/ncmecReporting.ts | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/server/services/ncmecService/ncmecReporting.ts b/server/services/ncmecService/ncmecReporting.ts index e6fc3982..79e9a297 100644 --- a/server/services/ncmecService/ncmecReporting.ts +++ b/server/services/ncmecService/ncmecReporting.ts @@ -1799,31 +1799,12 @@ export default class NcmecReporting { // failure, since we can't guarantee all traces with exceptions are // sampled in DD try { - // These are test accounts that we send to prospective users, and - // they should be able to click "Send to NCMEC" in the UI, but no - // NCMEC report should actually be created. - const testOrgs = ['4def6a77d6a', 'acc701627cb']; - if (!(await this.hasNCMECReportingEnabled(reportParams.orgId))) { throw new Error( `NCMEC reports are not enabled for org ${reportParams.orgId}`, ); } - if (testOrgs.includes(reportParams.orgId)) { - if (reportParams.jobId !== undefined) { - await this.#recordSubmissionError({ - jobId: reportParams.jobId, - userId: reportParams.reportedUser.id, - userTypeId: reportParams.reportedUser.typeId, - status: 'PERMANENT_ERROR', - error: - 'Org is on the NCMEC test allowlist; reports are suppressed.', - }); - } - return 'UNSUPPORTED_ORG'; - } - if (reportParams.media.length === 0) { throw new Error('No media in report'); } From a8b5d3680ddb6125c6b4c5f60e6929cca49bbddb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Jul 2026 15:06:59 +0100 Subject: [PATCH 19/57] build(deps): bump fast-uri (#939) Bumps the db-prod-security group with 1 update in the /db directory: [fast-uri](https://github.com/fastify/fast-uri). Updates `fast-uri` from 3.1.2 to 3.1.4 - [Release notes](https://github.com/fastify/fast-uri/releases) - [Commits](https://github.com/fastify/fast-uri/compare/v3.1.2...v3.1.4) --- updated-dependencies: - dependency-name: fast-uri dependency-version: 3.1.4 dependency-type: indirect dependency-group: db-prod-security ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- db/package-lock.json | 6 +++--- db/package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/db/package-lock.json b/db/package-lock.json index 9b72268c..9ccd5d3c 100644 --- a/db/package-lock.json +++ b/db/package-lock.json @@ -1263,9 +1263,9 @@ } }, "node_modules/fast-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", - "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.4.tgz", + "integrity": "sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw==", "funding": [ { "type": "github", diff --git a/db/package.json b/db/package.json index 894582de..c8c1a864 100644 --- a/db/package.json +++ b/db/package.json @@ -29,7 +29,7 @@ }, "overrides": { "ajv": "~8.18.0", - "fast-uri": "^3.1.2", + "fast-uri": "^3.1.4", "uuid": "^14.0.0" }, "devDependencies": { From 18fc618fdfdc184471b915ac0a8beab0af896a21 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Jul 2026 15:08:33 +0100 Subject: [PATCH 20/57] build(deps): bump fast-uri (#938) Bumps the migrator-prod-security group with 1 update in the /migrator directory: [fast-uri](https://github.com/fastify/fast-uri). Updates `fast-uri` from 3.1.2 to 3.1.4 - [Release notes](https://github.com/fastify/fast-uri/releases) - [Commits](https://github.com/fastify/fast-uri/compare/v3.1.2...v3.1.4) --- updated-dependencies: - dependency-name: fast-uri dependency-version: 3.1.4 dependency-type: indirect dependency-group: migrator-prod-security ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- migrator/package-lock.json | 63 +++++--------------------------------- migrator/package.json | 2 +- 2 files changed, 8 insertions(+), 57 deletions(-) diff --git a/migrator/package-lock.json b/migrator/package-lock.json index 6998442e..830fb331 100644 --- a/migrator/package-lock.json +++ b/migrator/package-lock.json @@ -237,9 +237,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -257,9 +254,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -277,9 +271,6 @@ "ppc64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -297,9 +288,6 @@ "riscv64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -317,9 +305,6 @@ "riscv64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -337,9 +322,6 @@ "s390x" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -357,9 +339,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -377,9 +356,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -592,9 +568,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -609,9 +582,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -626,9 +596,6 @@ "ppc64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -643,9 +610,6 @@ "riscv64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -660,9 +624,6 @@ "riscv64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -677,9 +638,6 @@ "s390x" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -694,9 +652,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -711,9 +666,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -1118,9 +1070,9 @@ } }, "node_modules/fast-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", - "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.4.tgz", + "integrity": "sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw==", "funding": [ { "type": "github", @@ -1130,8 +1082,7 @@ "type": "opencollective", "url": "https://opencollective.com/fastify" } - ], - "license": "BSD-3-Clause" + ] }, "node_modules/fastq": { "version": "1.20.1", @@ -2741,9 +2692,9 @@ } }, "fast-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", - "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==" + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.4.tgz", + "integrity": "sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw==" }, "fastq": { "version": "1.20.1", diff --git a/migrator/package.json b/migrator/package.json index b65b74ee..45c5481d 100644 --- a/migrator/package.json +++ b/migrator/package.json @@ -40,7 +40,7 @@ "overrides": { "ajv": "~8.18.0", "uuid": "^14.0.0", - "fast-uri": "^3.1.2" + "fast-uri": "^3.1.4" }, "publishConfig": { "access": "public" From f9f9cc1a2b3a470d3c7a93856ba4f0cd8d30db37 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Jul 2026 15:09:34 +0100 Subject: [PATCH 21/57] build(deps): bump undici from 7.27.1 to 7.29.0 in /client (#937) Bumps [undici](https://github.com/nodejs/undici) from 7.27.1 to 7.29.0. - [Release notes](https://github.com/nodejs/undici/releases) - [Commits](https://github.com/nodejs/undici/compare/v7.27.1...v7.29.0) --- updated-dependencies: - dependency-name: undici dependency-version: 7.29.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- client/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index dc54525b..b5572bed 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -14046,9 +14046,9 @@ } }, "node_modules/undici": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.27.1.tgz", - "integrity": "sha512-UDdpiex+mzigiyrXrGbiUaF4HzTNhKbh2vRNFaTMzcqmLIPrZxaCtwo/1TMSuWoM1Xz3WiTo9KdgI3kRqYzJGg==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.29.0.tgz", + "integrity": "sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==", "dev": true, "license": "MIT", "engines": { From 0a2a0333eb3b336b79dbc2c12c8144dc5bd4d4d0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Jul 2026 15:10:09 +0100 Subject: [PATCH 22/57] build(deps): bump immutable from 5.1.5 to 5.1.9 (#936) Bumps [immutable](https://github.com/immutable-js/immutable-js) from 5.1.5 to 5.1.9. - [Release notes](https://github.com/immutable-js/immutable-js/releases) - [Changelog](https://github.com/immutable-js/immutable-js/blob/main/CHANGELOG.md) - [Commits](https://github.com/immutable-js/immutable-js/compare/v5.1.5...v5.1.9) --- updated-dependencies: - dependency-name: immutable dependency-version: 5.1.9 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 60c2763a..2425e434 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3552,9 +3552,9 @@ } }, "node_modules/immutable": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.5.tgz", - "integrity": "sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A==", + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.9.tgz", + "integrity": "sha512-m8nVez3rwrgmWxtLMt1ZYXB2Lv7OKYn/disyxAlSDYAlKSlFoPPfIAmAM/M5xqL4m4C/wAPw7S2/CNaUii1Hxg==", "dev": true, "license": "MIT" }, From db73283e819b93534bed73338130f361bb43f040 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Jul 2026 15:10:59 +0100 Subject: [PATCH 23/57] build(deps): bump protobufjs (#935) Bumps the nodejs-instrumentation-prod-security group with 1 update in the /nodejs-instrumentation directory: [protobufjs](https://github.com/protobufjs/protobuf.js). Updates `protobufjs` from 8.6.4 to 8.7.1 - [Release notes](https://github.com/protobufjs/protobuf.js/releases) - [Changelog](https://github.com/protobufjs/protobuf.js/blob/master/CHANGELOG.md) - [Commits](https://github.com/protobufjs/protobuf.js/compare/protobufjs-v8.6.4...protobufjs-v8.7.1) --- updated-dependencies: - dependency-name: protobufjs dependency-version: 8.7.1 dependency-type: indirect dependency-group: nodejs-instrumentation-prod-security ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- nodejs-instrumentation/package-lock.json | 1060 ++++++++++++---------- nodejs-instrumentation/package.json | 8 +- 2 files changed, 579 insertions(+), 489 deletions(-) diff --git a/nodejs-instrumentation/package-lock.json b/nodejs-instrumentation/package-lock.json index b2adf911..3a9727c5 100644 --- a/nodejs-instrumentation/package-lock.json +++ b/nodejs-instrumentation/package-lock.json @@ -10,15 +10,15 @@ "license": "ISC", "dependencies": { "@opentelemetry/api": "^1.9.0", - "@opentelemetry/auto-instrumentations-node": "^0.77.0", - "@opentelemetry/exporter-metrics-otlp-grpc": "^0.219.0", - "@opentelemetry/exporter-trace-otlp-grpc": "^0.219.0", + "@opentelemetry/auto-instrumentations-node": "^0.79.0", + "@opentelemetry/exporter-metrics-otlp-grpc": "^0.221.0", + "@opentelemetry/exporter-trace-otlp-grpc": "^0.221.0", "@opentelemetry/propagator-aws-xray": "^2.2.0", "@opentelemetry/resource-detector-aws": "^2.19.0", "@opentelemetry/resource-detector-container": "^0.8.10", "@opentelemetry/resources": "^2.8.0", "@opentelemetry/sdk-metrics": "^2.8.0", - "@opentelemetry/sdk-node": "^0.219.0", + "@opentelemetry/sdk-node": "^0.221.0", "@opentelemetry/sdk-trace-base": "^2.8.0", "@opentelemetry/semantic-conventions": "^1.41.1", "@opentelemetry/winston-transport": "^0.29.0" @@ -116,60 +116,60 @@ } }, "node_modules/@opentelemetry/auto-instrumentations-node": { - "version": "0.77.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/auto-instrumentations-node/-/auto-instrumentations-node-0.77.0.tgz", - "integrity": "sha512-LkF930Cs+v+ZO/qV6LolbocvFkJJ812BBDyRNjQpwllBA+rFvGtP/voXPuh24QV3JKl5/3c3GulLHvMn8spqkQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.219.0", - "@opentelemetry/instrumentation-amqplib": "^0.66.0", - "@opentelemetry/instrumentation-aws-lambda": "^0.71.0", - "@opentelemetry/instrumentation-aws-sdk": "^0.74.0", - "@opentelemetry/instrumentation-bunyan": "^0.64.0", - "@opentelemetry/instrumentation-cassandra-driver": "^0.64.0", - "@opentelemetry/instrumentation-connect": "^0.62.0", - "@opentelemetry/instrumentation-cucumber": "^0.35.0", - "@opentelemetry/instrumentation-dataloader": "^0.36.0", - "@opentelemetry/instrumentation-dns": "^0.62.0", - "@opentelemetry/instrumentation-express": "^0.67.0", - "@opentelemetry/instrumentation-fs": "^0.38.0", - "@opentelemetry/instrumentation-generic-pool": "^0.62.0", - "@opentelemetry/instrumentation-graphql": "^0.67.0", - "@opentelemetry/instrumentation-grpc": "^0.219.0", - "@opentelemetry/instrumentation-hapi": "^0.65.0", - "@opentelemetry/instrumentation-host-metrics": "^0.2.0", - "@opentelemetry/instrumentation-http": "^0.219.0", - "@opentelemetry/instrumentation-ioredis": "^0.67.0", - "@opentelemetry/instrumentation-kafkajs": "^0.28.0", - "@opentelemetry/instrumentation-knex": "^0.63.0", - "@opentelemetry/instrumentation-koa": "^0.67.0", - "@opentelemetry/instrumentation-lru-memoizer": "^0.63.0", - "@opentelemetry/instrumentation-memcached": "^0.62.0", - "@opentelemetry/instrumentation-mongodb": "^0.72.0", - "@opentelemetry/instrumentation-mongoose": "^0.65.0", - "@opentelemetry/instrumentation-mysql": "^0.65.0", - "@opentelemetry/instrumentation-mysql2": "^0.65.0", - "@opentelemetry/instrumentation-nestjs-core": "^0.65.0", - "@opentelemetry/instrumentation-net": "^0.63.0", - "@opentelemetry/instrumentation-openai": "^0.17.0", - "@opentelemetry/instrumentation-oracledb": "^0.44.0", - "@opentelemetry/instrumentation-pg": "^0.71.0", - "@opentelemetry/instrumentation-pino": "^0.65.0", - "@opentelemetry/instrumentation-redis": "^0.67.0", - "@opentelemetry/instrumentation-restify": "^0.64.0", - "@opentelemetry/instrumentation-router": "^0.63.0", - "@opentelemetry/instrumentation-runtime-node": "^0.32.0", - "@opentelemetry/instrumentation-socket.io": "^0.66.0", - "@opentelemetry/instrumentation-tedious": "^0.38.0", - "@opentelemetry/instrumentation-undici": "^0.29.0", - "@opentelemetry/instrumentation-winston": "^0.63.0", - "@opentelemetry/resource-detector-alibaba-cloud": "^0.34.0", - "@opentelemetry/resource-detector-aws": "^2.19.0", - "@opentelemetry/resource-detector-azure": "^0.27.0", - "@opentelemetry/resource-detector-container": "^0.8.10", - "@opentelemetry/resource-detector-gcp": "^0.54.0", + "version": "0.79.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/auto-instrumentations-node/-/auto-instrumentations-node-0.79.0.tgz", + "integrity": "sha512-qL53aIjdw56sRDqz6LXD9h15vPTJgPpqv80rbsnRjzhuC9VqZ58fgk/lx0SdECJ2rcu8keeji5ZgjzJwiQZ0fg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/instrumentation-amqplib": "^0.68.0", + "@opentelemetry/instrumentation-aws-lambda": "^0.73.0", + "@opentelemetry/instrumentation-aws-sdk": "^0.76.0", + "@opentelemetry/instrumentation-bunyan": "^0.66.0", + "@opentelemetry/instrumentation-cassandra-driver": "^0.66.0", + "@opentelemetry/instrumentation-connect": "^0.64.0", + "@opentelemetry/instrumentation-cucumber": "^0.37.0", + "@opentelemetry/instrumentation-dataloader": "^0.38.0", + "@opentelemetry/instrumentation-dns": "^0.64.0", + "@opentelemetry/instrumentation-express": "^0.69.0", + "@opentelemetry/instrumentation-fs": "^0.40.0", + "@opentelemetry/instrumentation-generic-pool": "^0.64.0", + "@opentelemetry/instrumentation-graphql": "^0.69.0", + "@opentelemetry/instrumentation-grpc": "^0.221.0", + "@opentelemetry/instrumentation-hapi": "^0.67.0", + "@opentelemetry/instrumentation-host-metrics": "^0.4.0", + "@opentelemetry/instrumentation-http": "^0.221.0", + "@opentelemetry/instrumentation-ioredis": "^0.69.0", + "@opentelemetry/instrumentation-kafkajs": "^0.30.0", + "@opentelemetry/instrumentation-knex": "^0.65.0", + "@opentelemetry/instrumentation-koa": "^0.69.0", + "@opentelemetry/instrumentation-lru-memoizer": "^0.65.0", + "@opentelemetry/instrumentation-memcached": "^0.64.0", + "@opentelemetry/instrumentation-mongodb": "^0.74.0", + "@opentelemetry/instrumentation-mongoose": "^0.67.0", + "@opentelemetry/instrumentation-mysql": "^0.67.0", + "@opentelemetry/instrumentation-mysql2": "^0.67.0", + "@opentelemetry/instrumentation-nestjs-core": "^0.67.0", + "@opentelemetry/instrumentation-net": "^0.65.0", + "@opentelemetry/instrumentation-openai": "^0.19.0", + "@opentelemetry/instrumentation-oracledb": "^0.46.0", + "@opentelemetry/instrumentation-pg": "^0.73.0", + "@opentelemetry/instrumentation-pino": "^0.67.0", + "@opentelemetry/instrumentation-redis": "^0.69.0", + "@opentelemetry/instrumentation-restify": "^0.66.0", + "@opentelemetry/instrumentation-router": "^0.65.0", + "@opentelemetry/instrumentation-runtime-node": "^0.34.0", + "@opentelemetry/instrumentation-socket.io": "^0.68.0", + "@opentelemetry/instrumentation-tedious": "^0.40.0", + "@opentelemetry/instrumentation-undici": "^0.31.0", + "@opentelemetry/instrumentation-winston": "^0.65.0", + "@opentelemetry/resource-detector-alibaba-cloud": "^0.36.0", + "@opentelemetry/resource-detector-aws": "^2.21.0", + "@opentelemetry/resource-detector-azure": "^0.29.0", + "@opentelemetry/resource-detector-container": "^0.8.12", + "@opentelemetry/resource-detector-gcp": "^0.56.0", "@opentelemetry/resources": "^2.0.0", - "@opentelemetry/sdk-node": "^0.219.0" + "@opentelemetry/sdk-node": "^0.221.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -180,13 +180,13 @@ } }, "node_modules/@opentelemetry/configuration": { - "version": "0.219.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/configuration/-/configuration-0.219.0.tgz", - "integrity": "sha512-wXZUYv4ngu43nA4WEhuXNacm46LW+17LRM8nKyIhBzroRA24PBYjMnakwzR/w777nFUB5xlgsYTTeuXxumZM1Q==", + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/configuration/-/configuration-0.221.0.tgz", + "integrity": "sha512-uE9y56Zdi9Gt/RdxYnVOo3YmFZkKJJMA0gqtBe8wh8gdtF5Asqe+Oh/TWiDtFb1s+31jNY4CWgnfIB1KOITfFA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.8.0", - "yaml": "^2.0.0" + "@opentelemetry/core": "2.10.0", + "yaml": "^2.8.3" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -196,9 +196,9 @@ } }, "node_modules/@opentelemetry/context-async-hooks": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-2.8.0.tgz", - "integrity": "sha512-/3FIraneMcng67SUJCxvyInk/oxzwsxyadufk0wwfOBLf5wqtAGX4MoQASwSbndBPeARzBryUM9Azr5kHIdWLw==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-2.10.0.tgz", + "integrity": "sha512-bvyMcgLEkozzSzpEEEo1OMoeQ97bxj6Qs2uN3mPrSdDvObMI1myffD/BPqcLlzZO9//d1SqQA/WPw7Cz2AiqhA==", "license": "Apache-2.0", "engines": { "node": "^18.19.0 || >=20.6.0" @@ -208,9 +208,9 @@ } }, "node_modules/@opentelemetry/core": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.8.0.tgz", - "integrity": "sha512-hd1Lfh8p545nNz+jq1Ejfz+Mn1hyLuxYn1YzTfFNrxr8urEWMNQLPf1Th8kjOH+HxwawCrtgBp8JpBUR4ZSgww==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.10.0.tgz", + "integrity": "sha512-/wNZ8twnEQQA4HoHu22+vcsdru6pWPWxW+7w+FlxT6Id7PE/WIbZmVKkte+PF72e0F2dnImFeHD2syyE1Mw6MQ==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" @@ -223,17 +223,15 @@ } }, "node_modules/@opentelemetry/exporter-logs-otlp-grpc": { - "version": "0.219.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-grpc/-/exporter-logs-otlp-grpc-0.219.0.tgz", - "integrity": "sha512-7SvzDCIclHWAcCwZ1MTOLcwn4BVNPGI3QxS/DJraPNe1TTL+4TvUBq5zeQV8tsnYvtDN7wKW2qocVmaCP2l7sQ==", + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-grpc/-/exporter-logs-otlp-grpc-0.221.0.tgz", + "integrity": "sha512-txG1G0IrYSsKKMeiWZfj/i5cQmWB+h+hf3HzPpF3RqZVwp+iQQEIsv8Vtmzy6RWVdHdJZfygmVrBI39YTBvWcw==", "license": "Apache-2.0", "dependencies": { - "@grpc/grpc-js": "^1.14.3", - "@opentelemetry/core": "2.8.0", - "@opentelemetry/otlp-exporter-base": "0.219.0", - "@opentelemetry/otlp-grpc-exporter-base": "0.219.0", - "@opentelemetry/otlp-transformer": "0.219.0", - "@opentelemetry/sdk-logs": "0.219.0" + "@opentelemetry/otlp-exporter-base": "0.221.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.221.0", + "@opentelemetry/otlp-transformer": "0.221.0", + "@opentelemetry/sdk-logs": "0.221.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -243,16 +241,14 @@ } }, "node_modules/@opentelemetry/exporter-logs-otlp-http": { - "version": "0.219.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-http/-/exporter-logs-otlp-http-0.219.0.tgz", - "integrity": "sha512-mhl2HL6GmZI8b8PwPfqMws/5ovJfbRTxwc9Y5agVVHiQ+e5SL1btsFr/kJDgt7YCexDtsUn5HAreHQO9szFS0A==", + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-http/-/exporter-logs-otlp-http-0.221.0.tgz", + "integrity": "sha512-nKXkr4Tomi6fjYVOf+ytcW3dZAVr4v4Bv5gsT6dr2gvpUPJpKgHB4XbMufMsPotRE3g0XH2GwVVCkN2w6SON+Q==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api-logs": "0.219.0", - "@opentelemetry/core": "2.8.0", - "@opentelemetry/otlp-exporter-base": "0.219.0", - "@opentelemetry/otlp-transformer": "0.219.0", - "@opentelemetry/sdk-logs": "0.219.0" + "@opentelemetry/otlp-exporter-base": "0.221.0", + "@opentelemetry/otlp-transformer": "0.221.0", + "@opentelemetry/sdk-logs": "0.221.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -262,18 +258,14 @@ } }, "node_modules/@opentelemetry/exporter-logs-otlp-proto": { - "version": "0.219.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-proto/-/exporter-logs-otlp-proto-0.219.0.tgz", - "integrity": "sha512-Ayw4Gf71PS9jhBVaYywa4WsajnqfDehMkTdVH3TSAVHqPcsAv/AhH/wTNRYNt99szeYr6Gbd/D6RjZD77wAxHg==", + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-proto/-/exporter-logs-otlp-proto-0.221.0.tgz", + "integrity": "sha512-AH6EY+47gXFaWYgG3hfeOneGiE9xIZGtDBk+9g0sM8NZWzsQhhmqPbQQXJzS7pyCh5jRRr2nYNXVrkCmoojRvQ==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api-logs": "0.219.0", - "@opentelemetry/core": "2.8.0", - "@opentelemetry/otlp-exporter-base": "0.219.0", - "@opentelemetry/otlp-transformer": "0.219.0", - "@opentelemetry/resources": "2.8.0", - "@opentelemetry/sdk-logs": "0.219.0", - "@opentelemetry/sdk-trace-base": "2.8.0" + "@opentelemetry/otlp-exporter-base": "0.221.0", + "@opentelemetry/otlp-transformer": "0.221.0", + "@opentelemetry/sdk-logs": "0.221.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -283,19 +275,14 @@ } }, "node_modules/@opentelemetry/exporter-metrics-otlp-grpc": { - "version": "0.219.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-grpc/-/exporter-metrics-otlp-grpc-0.219.0.tgz", - "integrity": "sha512-6LaaSrPxK5L55bXevWajvOMxGOpNm0n12tG53TeZaUeNzXwLPg6d2KCC1zAlGsojan+xRG71mA4Qqs9K2VVrKQ==", + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-grpc/-/exporter-metrics-otlp-grpc-0.221.0.tgz", + "integrity": "sha512-KOgCtO15FC6C1T/xOqBcr7EyUs7B+7yomGNb5Y97d3s38rPbCCk5sewkmE2b0/itOkQ/PptX8CLlD+kn2mEtTg==", "license": "Apache-2.0", "dependencies": { - "@grpc/grpc-js": "^1.14.3", - "@opentelemetry/core": "2.8.0", - "@opentelemetry/exporter-metrics-otlp-http": "0.219.0", - "@opentelemetry/otlp-exporter-base": "0.219.0", - "@opentelemetry/otlp-grpc-exporter-base": "0.219.0", - "@opentelemetry/otlp-transformer": "0.219.0", - "@opentelemetry/resources": "2.8.0", - "@opentelemetry/sdk-metrics": "2.8.0" + "@opentelemetry/exporter-metrics-otlp-http": "0.221.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.221.0", + "@opentelemetry/otlp-transformer": "0.221.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -305,16 +292,16 @@ } }, "node_modules/@opentelemetry/exporter-metrics-otlp-http": { - "version": "0.219.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-http/-/exporter-metrics-otlp-http-0.219.0.tgz", - "integrity": "sha512-6CaDRbMVHZSDWzNXwrR8y/H4B/Z1eMNnkHiPQlTx3Ojz2OHY4X/aff/UC4P/3pHUQSuTfi3oh2UsPPZppw+Vrg==", + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-http/-/exporter-metrics-otlp-http-0.221.0.tgz", + "integrity": "sha512-sRfCKbOzgy8xZQV2as0RzIZlnCmCseCKZGLfRcrpo2CBngJDr+rPtX0zkG0+oUCV5kfQPUoW3W3C96Ag3Y/Clg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.8.0", - "@opentelemetry/otlp-exporter-base": "0.219.0", - "@opentelemetry/otlp-transformer": "0.219.0", - "@opentelemetry/resources": "2.8.0", - "@opentelemetry/sdk-metrics": "2.8.0" + "@opentelemetry/core": "2.10.0", + "@opentelemetry/otlp-exporter-base": "0.221.0", + "@opentelemetry/otlp-transformer": "0.221.0", + "@opentelemetry/resources": "2.10.0", + "@opentelemetry/sdk-metrics": "2.10.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -324,17 +311,14 @@ } }, "node_modules/@opentelemetry/exporter-metrics-otlp-proto": { - "version": "0.219.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-proto/-/exporter-metrics-otlp-proto-0.219.0.tgz", - "integrity": "sha512-DUS7XyIiEnoeccQUvuKy0G2/YqeKhpN8FVIrGbrLNIVMj10yeIFLRzRv0tibCI2kXXvlTTABVexGAk78wHk2ug==", + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-proto/-/exporter-metrics-otlp-proto-0.221.0.tgz", + "integrity": "sha512-YMF4LveY2I3yhw61rn6nmC9FE8U24IZHPeKU1Duc5+sbwjMd8FwZAwba318ImdThCg/HuVQvhm2y6bfgNPnfYg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.8.0", - "@opentelemetry/exporter-metrics-otlp-http": "0.219.0", - "@opentelemetry/otlp-exporter-base": "0.219.0", - "@opentelemetry/otlp-transformer": "0.219.0", - "@opentelemetry/resources": "2.8.0", - "@opentelemetry/sdk-metrics": "2.8.0" + "@opentelemetry/exporter-metrics-otlp-http": "0.221.0", + "@opentelemetry/otlp-exporter-base": "0.221.0", + "@opentelemetry/otlp-transformer": "0.221.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -344,14 +328,14 @@ } }, "node_modules/@opentelemetry/exporter-prometheus": { - "version": "0.219.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-prometheus/-/exporter-prometheus-0.219.0.tgz", - "integrity": "sha512-TxOnJ85eWJY5JyOJsNMXiRTYlkDcOv0u3KbXEzWCc+tUS9sjL/BC6BcdxZ0B9r2OFVqsrZFXUzSD2sZUy42Ucw==", + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-prometheus/-/exporter-prometheus-0.221.0.tgz", + "integrity": "sha512-kW79a20qWESIuAdDrxzg9WKM98twV/NBWBFRAH57ap/+ssZhiCo0hckzKT0zpuwR/gSHrFAQhJL0bYDrnEM34g==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.8.0", - "@opentelemetry/resources": "2.8.0", - "@opentelemetry/sdk-metrics": "2.8.0", + "@opentelemetry/core": "2.10.0", + "@opentelemetry/resources": "2.10.0", + "@opentelemetry/sdk-metrics": "2.10.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { @@ -362,18 +346,15 @@ } }, "node_modules/@opentelemetry/exporter-trace-otlp-grpc": { - "version": "0.219.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.219.0.tgz", - "integrity": "sha512-BkDNv1UD6BscW19MxbAxVmSYSSFuyeqR6buV2/HTYqA7GrR0EbTFzqG6h86T3PtXmpdbsWjMGLDdjG2rikG27Q==", + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.221.0.tgz", + "integrity": "sha512-zXminlZedtq9LvOW64CnNkOqk15zV75k8JgtdTuWFge6+jk2m4GmAUm6L2eIiG1o2a2bZxXw2PDrszm+bps0IA==", "license": "Apache-2.0", "dependencies": { - "@grpc/grpc-js": "^1.14.3", - "@opentelemetry/core": "2.8.0", - "@opentelemetry/otlp-exporter-base": "0.219.0", - "@opentelemetry/otlp-grpc-exporter-base": "0.219.0", - "@opentelemetry/otlp-transformer": "0.219.0", - "@opentelemetry/resources": "2.8.0", - "@opentelemetry/sdk-trace-base": "2.8.0" + "@opentelemetry/otlp-exporter-base": "0.221.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.221.0", + "@opentelemetry/otlp-transformer": "0.221.0", + "@opentelemetry/sdk-trace": "2.10.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -383,16 +364,14 @@ } }, "node_modules/@opentelemetry/exporter-trace-otlp-http": { - "version": "0.219.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.219.0.tgz", - "integrity": "sha512-9t6SvBXXBEjOBcIzgozvBbd3jWrv3Gt3ngGhl1fhdZ/zRc7oZDVOFEqbi2zlBpW9BXhgDMKv422J0DL/3iQWfw==", + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.221.0.tgz", + "integrity": "sha512-AySXiKoC+meiWm6zdVj5T2LnPDZuatveBby1cMOeQteIWsYXAUxs8Sru13G2pVSPrUXz6vF+og7QVBX6GdC/oQ==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.8.0", - "@opentelemetry/otlp-exporter-base": "0.219.0", - "@opentelemetry/otlp-transformer": "0.219.0", - "@opentelemetry/resources": "2.8.0", - "@opentelemetry/sdk-trace-base": "2.8.0" + "@opentelemetry/otlp-exporter-base": "0.221.0", + "@opentelemetry/otlp-transformer": "0.221.0", + "@opentelemetry/sdk-trace": "2.10.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -402,16 +381,14 @@ } }, "node_modules/@opentelemetry/exporter-trace-otlp-proto": { - "version": "0.219.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-proto/-/exporter-trace-otlp-proto-0.219.0.tgz", - "integrity": "sha512-lF/LUBfhOFmxJa+SQsLN7ziV4MHa2pyKgOM6JNehSOfU+npjM4gwm9oIKEJrzrWcexMcqydiyoFy0XCb1Ql3wQ==", + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-proto/-/exporter-trace-otlp-proto-0.221.0.tgz", + "integrity": "sha512-Z9i2T7vgZbWe9rSLYxXVIbeW+XyzUq4rZanW3ZyVNwVDqCsh0EJKUgBWWQ0CZfeuUA+RQPzKgJQHMuWAUnKqXw==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.8.0", - "@opentelemetry/otlp-exporter-base": "0.219.0", - "@opentelemetry/otlp-transformer": "0.219.0", - "@opentelemetry/resources": "2.8.0", - "@opentelemetry/sdk-trace-base": "2.8.0" + "@opentelemetry/otlp-exporter-base": "0.221.0", + "@opentelemetry/otlp-transformer": "0.221.0", + "@opentelemetry/sdk-trace": "2.10.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -421,14 +398,14 @@ } }, "node_modules/@opentelemetry/exporter-zipkin": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-zipkin/-/exporter-zipkin-2.8.0.tgz", - "integrity": "sha512-Mj84UkEa17BK2o903VTXW3wM8CrSZexGs4tRGVZVIMM9ni1T6TuGx5IrRfoWKAbshx42D5/kc7YV+axypLPYyA==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-zipkin/-/exporter-zipkin-2.10.0.tgz", + "integrity": "sha512-7gsvgf0UDoJ4l9ObrwBmz5G/ZogiPk+lq+g5GpLp24YQF/vPM/BSsnOfcLnfinast5ASUgLo78uSC/ObjlnXgg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.8.0", - "@opentelemetry/resources": "2.8.0", - "@opentelemetry/sdk-trace-base": "2.8.0", + "@opentelemetry/core": "2.10.0", + "@opentelemetry/resources": "2.10.0", + "@opentelemetry/sdk-trace": "2.10.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { @@ -439,12 +416,12 @@ } }, "node_modules/@opentelemetry/instrumentation": { - "version": "0.219.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.219.0.tgz", - "integrity": "sha512-X5t7I8GyIO9rmGHwoedZLREpQqrF1WW2nxzNNym6HOKpFiE+rvqV3ngC0xcZVO2YwIGf3KKmRdWrYwdwz3H9RQ==", + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.221.0.tgz", + "integrity": "sha512-cCk80Z/iRDf/5gfsKMB4f74LqVA5yKETB/9ojPzVW/6/f70iu89nJvGxsFCxx4XfSohaOofkU19kiYm84AiAlw==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api-logs": "0.219.0", + "@opentelemetry/api-logs": "0.221.0", "import-in-the-middle": "^3.0.0", "require-in-the-middle": "^8.0.0" }, @@ -456,13 +433,13 @@ } }, "node_modules/@opentelemetry/instrumentation-amqplib": { - "version": "0.66.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-amqplib/-/instrumentation-amqplib-0.66.0.tgz", - "integrity": "sha512-lyJgobzP0Ce+tRGOkdnrb60apfqU89xB9FeMTmo1TJU007KTMLiLFd4iCfTiBEXzBsVplx7kwrVN4FqGBUcD2Q==", + "version": "0.68.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-amqplib/-/instrumentation-amqplib-0.68.0.tgz", + "integrity": "sha512-U9Fc3C061q+AGxP3xEJTIAJtBduY1GL21J4SjOSxCmmls4UUva16jzQ5ZkunQe0pKalrRjZ/DlZ+wgfgQxqjBw==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/instrumentation": "^0.221.0", "@opentelemetry/semantic-conventions": "^1.33.0" }, "engines": { @@ -473,12 +450,12 @@ } }, "node_modules/@opentelemetry/instrumentation-aws-lambda": { - "version": "0.71.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-aws-lambda/-/instrumentation-aws-lambda-0.71.0.tgz", - "integrity": "sha512-9Sv6flQDeNNF6ZbiLgn+NYJa220yRZdDSIdgDZsqNubpDnYAPF33OHcQDlE8mFiaOq14mngoPS48JnYc8JT+Ug==", + "version": "0.73.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-aws-lambda/-/instrumentation-aws-lambda-0.73.0.tgz", + "integrity": "sha512-N2BZFlWmVt2zjpiqPnfmIlj7tV/wfKSZCFF/laLAnJSTZjSEdc9JYSXW+KUV0FMUNDfpLCeAcI1xDMdGLdxFJg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/instrumentation": "^0.221.0", "@opentelemetry/propagator-aws-xray": "^2.1.4", "@opentelemetry/semantic-conventions": "^1.27.0", "@types/aws-lambda": "^8.10.155" @@ -491,13 +468,13 @@ } }, "node_modules/@opentelemetry/instrumentation-aws-sdk": { - "version": "0.74.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-aws-sdk/-/instrumentation-aws-sdk-0.74.0.tgz", - "integrity": "sha512-EMLUGgx2wJSXdwMEFdwd3IaW+mkUF8PENdzDFQ1FRdztzMG1d1XN76ORIjAMsXcKQISlRRcz93AWPQeBPn4EKA==", + "version": "0.76.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-aws-sdk/-/instrumentation-aws-sdk-0.76.0.tgz", + "integrity": "sha512-gg2QaDtWeFezRt2mAl9vBQ38y60tzUShKg1KA9uTgsMJimUHaBnB3X8kEH9Of8jKWT4DDoDcSA37gPcoEc0hiA==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/instrumentation": "^0.221.0", "@opentelemetry/semantic-conventions": "^1.34.0" }, "engines": { @@ -508,13 +485,14 @@ } }, "node_modules/@opentelemetry/instrumentation-bunyan": { - "version": "0.64.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-bunyan/-/instrumentation-bunyan-0.64.0.tgz", - "integrity": "sha512-jrRNFvpREutmpoWhk1T8n9q/RYdxbViXwSUPHN8yQR1bzgtwfOl/y8G/p8Xfudlky9GGsqw5WRc6q6QrfgF3pw==", + "version": "0.66.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-bunyan/-/instrumentation-bunyan-0.66.0.tgz", + "integrity": "sha512-IqYQC1dav35NHlD5nYnpBXK8tI6KJ9/MIt8LYKFxwlhMIwfBCfavaklyP8NtVKoJ0WZKzX2v9Sh+xGK1XvSniQ==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api-logs": "^0.219.0", - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/api-logs": "^0.221.0", + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/semantic-conventions": "^1.41.1", "@types/bunyan": "1.8.11" }, "engines": { @@ -524,13 +502,25 @@ "@opentelemetry/api": "^1.3.0" } }, + "node_modules/@opentelemetry/instrumentation-bunyan/node_modules/@opentelemetry/api-logs": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.221.0.tgz", + "integrity": "sha512-OlanaW1vv7ufTqQ3/fPLI4arGt5ZoM+P8abOMki6uEYnpRazepSWDwDnnw+la7kE26SHVC18//SMccrDvLKOXQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/@opentelemetry/instrumentation-cassandra-driver": { - "version": "0.64.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-cassandra-driver/-/instrumentation-cassandra-driver-0.64.0.tgz", - "integrity": "sha512-KN+iOsmPI0nkX2lfgNgHBrHNaDuxDwIbwFrlvyrZ4bAT8bTKcCOXhne70O9qihM8+T1F4tjvPXpCfhKZbmsYiw==", + "version": "0.66.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-cassandra-driver/-/instrumentation-cassandra-driver-0.66.0.tgz", + "integrity": "sha512-4ksN7PfXLg7raDyXIjIrtxxzuuDlUx6Fh0s8VXojdl+nn2o0xkYa9jrq1f9Bfqhm+Sce3GOa1RjE3ycvAwk6Xw==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/instrumentation": "^0.221.0", "@opentelemetry/semantic-conventions": "^1.37.0" }, "engines": { @@ -541,13 +531,13 @@ } }, "node_modules/@opentelemetry/instrumentation-connect": { - "version": "0.62.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-connect/-/instrumentation-connect-0.62.0.tgz", - "integrity": "sha512-ZGV2sOyeffqMiqoh4RpsPTs/TUI5cCS+cEWvC9wUfvaEekR5omR6P/ClG+QDwasGBlKx2zfFPjSYPpzUo81XAw==", + "version": "0.64.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-connect/-/instrumentation-connect-0.64.0.tgz", + "integrity": "sha512-D1Tpom3BpY8g29FFOEQ2FZioVFjyXwXHsh3BOn2BHcg7Taipg+yc+DPGUwvdR4WZrKnNMAG/+FBXNa0S0KhJYA==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/instrumentation": "^0.221.0", "@opentelemetry/semantic-conventions": "^1.27.0", "@types/connect": "3.4.38" }, @@ -559,12 +549,12 @@ } }, "node_modules/@opentelemetry/instrumentation-cucumber": { - "version": "0.35.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-cucumber/-/instrumentation-cucumber-0.35.0.tgz", - "integrity": "sha512-H9NsbcFiVOFOcOu40+VOOjxdTeonu0VHI3mte5ie5ka/bazzIPGncQoHpL6su53C3/sgMdKjE6ZuwsB7Y8gQpA==", + "version": "0.37.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-cucumber/-/instrumentation-cucumber-0.37.0.tgz", + "integrity": "sha512-ezhn6D0DSUZkwLBGdfnr+PAENvB92AhbEH/dkcSLYB8dbQiw/NQ8y19jOn3E6MZTt0+FD1YyHtrJS2skDO4nDQ==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/instrumentation": "^0.221.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "engines": { @@ -575,12 +565,12 @@ } }, "node_modules/@opentelemetry/instrumentation-dataloader": { - "version": "0.36.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dataloader/-/instrumentation-dataloader-0.36.0.tgz", - "integrity": "sha512-gE0mTk+EnVaBN0mPRM1V6FqzQ9VckTp6ZFIssU5hxy+e3sqspYILBhV/0IHZ33qxGa3B9buLVZnuzVjUISfyoQ==", + "version": "0.38.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dataloader/-/instrumentation-dataloader-0.38.0.tgz", + "integrity": "sha512-OmOVadK0m7sdlvMwbt1gb2iVUCyvVNDo3x5JLGgnKggLTJBgTcQxgMl3pAhFAMzWGo9URuMxuh3Bphy9Pb9nZw==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.219.0" + "@opentelemetry/instrumentation": "^0.221.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -590,12 +580,12 @@ } }, "node_modules/@opentelemetry/instrumentation-dns": { - "version": "0.62.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dns/-/instrumentation-dns-0.62.0.tgz", - "integrity": "sha512-6v0X8wEqhIyv2b7MXhmipyCitJfm0vnF5mLBTWXojovoHp7P1EpN5sb12LgdhVlozhGwRZdzb6OvKUVsxKMD8g==", + "version": "0.64.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dns/-/instrumentation-dns-0.64.0.tgz", + "integrity": "sha512-La6s9SdKgojZQVFD7AclQBYe3WioVe6zicJswM3QPPPHpCufJYnw8rO/G9o2Yl/OUeS7PYpzwHh4N6lexzbEcA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.219.0" + "@opentelemetry/instrumentation": "^0.221.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -605,13 +595,13 @@ } }, "node_modules/@opentelemetry/instrumentation-express": { - "version": "0.67.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-express/-/instrumentation-express-0.67.0.tgz", - "integrity": "sha512-1WTWX2YNZIV+jPmEqdf/Vd1gHMT92TKA/0pf/iIItWhV6+RhzhnUUW4kSWQn8L3qVcgWEzQ860/ZOwaIwayi8A==", + "version": "0.69.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-express/-/instrumentation-express-0.69.0.tgz", + "integrity": "sha512-91pHMujQgDyhEQrdg8RriMBrRZ/qPaJ0Y2dopQ6lHjW5YjoeytWi8ruM//T6f5o0D95hnqRlv79Pel1lGPqaYg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/core": "^2.9.0", + "@opentelemetry/instrumentation": "^0.221.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "engines": { @@ -622,13 +612,13 @@ } }, "node_modules/@opentelemetry/instrumentation-fs": { - "version": "0.38.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fs/-/instrumentation-fs-0.38.0.tgz", - "integrity": "sha512-6OBofWODg0RcPkl3bA+7yPf0e4Vi3O7ZxlFGY5QHPMMLxWVMnjWPEXQ2NlLBk19Sr8LcvEyyZOStdLJrT5o2dQ==", + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fs/-/instrumentation-fs-0.40.0.tgz", + "integrity": "sha512-p39axaaYVKhnl5l4M+1aiXmxrAG2HuTti7DHxs2jDJRst828y5iwqUZLC1UWIKIhW9FfdV5gogXg+nRRhSc0EA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.219.0" + "@opentelemetry/core": "^2.9.0", + "@opentelemetry/instrumentation": "^0.221.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -638,12 +628,12 @@ } }, "node_modules/@opentelemetry/instrumentation-generic-pool": { - "version": "0.62.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-generic-pool/-/instrumentation-generic-pool-0.62.0.tgz", - "integrity": "sha512-IhO2y/MaK1oZ4EbUgdkSVT+XViPq86IAz4lwPe9jaba00M2yDWs8+f9xjcH3tK5IC3dZuAxXmifGmfvuJp92jw==", + "version": "0.64.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-generic-pool/-/instrumentation-generic-pool-0.64.0.tgz", + "integrity": "sha512-wM939j8Ox5BBHoA0r/p9etdpyS3GcUf/sfrUx1dtZdqGKU4ZcBxLyPD8QntwFkSI9PHql+rRejTCA6Btktz8Kg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.219.0" + "@opentelemetry/instrumentation": "^0.221.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -653,12 +643,12 @@ } }, "node_modules/@opentelemetry/instrumentation-graphql": { - "version": "0.67.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-graphql/-/instrumentation-graphql-0.67.0.tgz", - "integrity": "sha512-NMUmuhtYvv3AwkK4zsHQbTCXS81QS63hbNZRKfXc7W8f4KbWeKLmqKqvnfjUcqZoGS0eAw2kNKNYcbtbQ7baAg==", + "version": "0.69.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-graphql/-/instrumentation-graphql-0.69.0.tgz", + "integrity": "sha512-vyKzuiBoEulV1FjMSe4iiuwZedt+nNAuaSVOh/3WxjDIuGQ9WsH+0ohd9snhIY6guAjYOGjqvTiYLs6K+OTQQg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.219.0" + "@opentelemetry/instrumentation": "^0.221.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -668,12 +658,12 @@ } }, "node_modules/@opentelemetry/instrumentation-grpc": { - "version": "0.219.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-grpc/-/instrumentation-grpc-0.219.0.tgz", - "integrity": "sha512-GyW1Kfbf7uiJXeBZovB/uXPUdkaZYWzB2ZPCdY2CU7+6V207u8wlCM+zVd3UwhEjOBrMbZAGq+m38aBEI/EVtA==", + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-grpc/-/instrumentation-grpc-0.221.0.tgz", + "integrity": "sha512-1U45172SiPWG1MPfrgLItIuXZO/RfJqt5sxsrdlKN1NRV0pUtv7lbpgB1nShwP/SGSKaAFkovbVg3a1hfy3mpQ==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "0.219.0", + "@opentelemetry/instrumentation": "0.221.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { @@ -684,13 +674,13 @@ } }, "node_modules/@opentelemetry/instrumentation-hapi": { - "version": "0.65.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-hapi/-/instrumentation-hapi-0.65.0.tgz", - "integrity": "sha512-Whhas9iU0SfK/7XBcgCwfW5c9AaxjxtZpzW21t3Ml8XZ6Irc3hF36JDolMmFa7nUjML9n9bSUAZjwCgrK+2UdQ==", + "version": "0.67.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-hapi/-/instrumentation-hapi-0.67.0.tgz", + "integrity": "sha512-cIXIN4vZXm6aI4yz+4oUIRnkiAxCIpONrMhnGTI+ILKKEsIXP8Uselfr9663+TnisrgRLB7kKp+SoOuGJRHGtw==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/core": "^2.9.0", + "@opentelemetry/instrumentation": "^0.221.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "engines": { @@ -701,12 +691,12 @@ } }, "node_modules/@opentelemetry/instrumentation-host-metrics": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-host-metrics/-/instrumentation-host-metrics-0.2.0.tgz", - "integrity": "sha512-NIttCEOLdg1ebbDiJpCf0Ly1OGIa10isesik+K2dnXy2P99q4muUFjpaLtTnhkENrt9SmR0Zrxzq7B+W/VNWyw==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-host-metrics/-/instrumentation-host-metrics-0.4.0.tgz", + "integrity": "sha512-jnFyX2sTn2B+9mjsL3qgAHzpxdaia4/FV2GSlf9QrpaiMi6O0M0lA9JZTsf4FTvuOwrIngCk+MeVXjsxgBXXyg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/instrumentation": "^0.221.0", "systeminformation": "^5.31.6" }, "engines": { @@ -717,13 +707,13 @@ } }, "node_modules/@opentelemetry/instrumentation-http": { - "version": "0.219.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-http/-/instrumentation-http-0.219.0.tgz", - "integrity": "sha512-nNt1fqpyah/OKjNHdEOu8xLwISppRU2qJuF8aR+fCcftVwdFkPgtworBLA+TI1HU2iF508jcQBF2gerWczJAXg==", + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-http/-/instrumentation-http-0.221.0.tgz", + "integrity": "sha512-oIP91CPIANuYr09tGFElPFKAh6JUar+awJf1kBRYlaeo9b0gDwZHEB2zBfFlvdNFHm0wAVutMZODVi5smKT30g==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.8.0", - "@opentelemetry/instrumentation": "0.219.0", + "@opentelemetry/core": "2.10.0", + "@opentelemetry/instrumentation": "0.221.0", "@opentelemetry/semantic-conventions": "^1.29.0", "forwarded-parse": "2.1.2" }, @@ -735,12 +725,12 @@ } }, "node_modules/@opentelemetry/instrumentation-ioredis": { - "version": "0.67.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-ioredis/-/instrumentation-ioredis-0.67.0.tgz", - "integrity": "sha512-dv64vQ4aXbJvRMMAFrMUSzDeJrNv/uQMLjfaav4LHAOar7Xn08W3pkoYoYEnzq/n2+fGgG96rp9S0gQ+VnLDiw==", + "version": "0.69.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-ioredis/-/instrumentation-ioredis-0.69.0.tgz", + "integrity": "sha512-I9sZtxXWZ1tRXtRNTEVxpokGtXy6RL1SZhtPVh7zxH78t8ar71V5Dx4bnQiUjKTDzItpC73krD8c0/cEWA9oLg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/instrumentation": "^0.221.0", "@opentelemetry/redis-common": "^0.38.3", "@opentelemetry/semantic-conventions": "^1.33.0" }, @@ -752,12 +742,12 @@ } }, "node_modules/@opentelemetry/instrumentation-kafkajs": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-kafkajs/-/instrumentation-kafkajs-0.28.0.tgz", - "integrity": "sha512-dztkg70nJds3Uc0Xo3NFlRqL5iYgGYWh8myuuGfRC6NnXJchY0Kw9QnBjTZxBSldXU+P6nv2snDVMmlxuy6fEw==", + "version": "0.30.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-kafkajs/-/instrumentation-kafkajs-0.30.0.tgz", + "integrity": "sha512-/p/D4etxJpJGB0VrS+kqF8WfVAMFWf5ybhY0mjzIEd/d/T68+nxTWJaI5MJXFiyOnBUFuMlun12VJt+QJDCSZA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/instrumentation": "^0.221.0", "@opentelemetry/semantic-conventions": "^1.30.0" }, "engines": { @@ -768,12 +758,12 @@ } }, "node_modules/@opentelemetry/instrumentation-knex": { - "version": "0.63.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-knex/-/instrumentation-knex-0.63.0.tgz", - "integrity": "sha512-XrpRahI/9vTrfSUfkhy8jGX8KMRKecQIPU9GyEZ8gkR030iJwQYsMmKGO5TK9R80cQGUopXwDvV55zmVNEkcPg==", + "version": "0.65.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-knex/-/instrumentation-knex-0.65.0.tgz", + "integrity": "sha512-rJTT12VlDnL6wOWfxnBvkTUIzW2ju+7nqlToMy1tlqL0j6ohlVPxryMCS5h6UqE3CFpq7HtHqVObQHZgrA37KA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/instrumentation": "^0.221.0", "@opentelemetry/semantic-conventions": "^1.33.1" }, "engines": { @@ -784,13 +774,13 @@ } }, "node_modules/@opentelemetry/instrumentation-koa": { - "version": "0.67.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-koa/-/instrumentation-koa-0.67.0.tgz", - "integrity": "sha512-QOGY4mjqvF85LDcrzwrQXMcsu1wMTALeL1OHyTkLpN/7cnoDtv0W/qMBjHVq4IKYK6yDH4ZDNdwlonJPhwCGcw==", + "version": "0.69.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-koa/-/instrumentation-koa-0.69.0.tgz", + "integrity": "sha512-fxuA8jFOdqQzJV9Sitd0dk+zns7RQCFe19ia3LHex5oLiQPaaQovBv37jndX/zAZw6EBORATePHE8OQUwraPCQ==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/core": "^2.9.0", + "@opentelemetry/instrumentation": "^0.221.0", "@opentelemetry/semantic-conventions": "^1.36.0" }, "engines": { @@ -801,12 +791,12 @@ } }, "node_modules/@opentelemetry/instrumentation-lru-memoizer": { - "version": "0.63.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-lru-memoizer/-/instrumentation-lru-memoizer-0.63.0.tgz", - "integrity": "sha512-DlZRNXfiosmREoLbEGbYuxF70cYXjrYqoaO1sJE167i1+ARWXTq0YMPJ97i53Ws/xkZWllJNYU4mpY2LM2yTlA==", + "version": "0.65.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-lru-memoizer/-/instrumentation-lru-memoizer-0.65.0.tgz", + "integrity": "sha512-s2KisLZ82iDvCF2QbsV1k1wrz3DMSBP9OiMfmNn5oSyaNT7jcNphR4uxr7WjwH+ssucuvhKWqKzJ2vdx7KRMVA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.219.0" + "@opentelemetry/instrumentation": "^0.221.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -816,12 +806,12 @@ } }, "node_modules/@opentelemetry/instrumentation-memcached": { - "version": "0.62.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-memcached/-/instrumentation-memcached-0.62.0.tgz", - "integrity": "sha512-kAajd/MtdRBh9PCrMM4fnusFRNPJDU1dv5w9cgnKtMfutRE6K03wBbGSeT3FD1M56sMYWVqPhiKUhfSZCMZrLg==", + "version": "0.64.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-memcached/-/instrumentation-memcached-0.64.0.tgz", + "integrity": "sha512-ek34tp7Qjci4CLahXybJ3aaixU1d2j28X5JSXSXbp6/rIiFGjMCAXJWw3FeiYmA82D/gV/wzPhL7r7m/p4gUzw==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/instrumentation": "^0.221.0", "@opentelemetry/semantic-conventions": "^1.33.0", "@types/memcached": "^2.2.6" }, @@ -833,12 +823,12 @@ } }, "node_modules/@opentelemetry/instrumentation-mongodb": { - "version": "0.72.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.72.0.tgz", - "integrity": "sha512-WYgGzvlHzdoxHlrhysYtjxE4RC23j/iFZ66hdMJuAoczOWoD/xb6LhRwaz4CM+LKyKtcSIadAjSUyGv2B+SNwg==", + "version": "0.74.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.74.0.tgz", + "integrity": "sha512-GRnHu69YLQUYgguuYkKi6wpizMY4r7gLC08rSq8cg41p6t7+1YIy5nXoGC61NA7KCZUE9jbcDnzd45i32IblZg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/instrumentation": "^0.221.0", "@opentelemetry/semantic-conventions": "^1.33.0" }, "engines": { @@ -849,13 +839,13 @@ } }, "node_modules/@opentelemetry/instrumentation-mongoose": { - "version": "0.65.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongoose/-/instrumentation-mongoose-0.65.0.tgz", - "integrity": "sha512-P0iT4oKuinEFZlTIKPJC5hhnmiV9De9lEiLkGzSwnNLdkyHIWug8BfRp5ZROrYAQ9mm47H+WLB/ozvUvgzEvEA==", + "version": "0.67.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongoose/-/instrumentation-mongoose-0.67.0.tgz", + "integrity": "sha512-iEBgNrychD36qI16X/V8WZb2JabjQPE+pyrkLU320ApZyhGVsYU9LH3Nqt4Mkg1nFsa9qWhRQrCfhoANCgT6EA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/core": "^2.9.0", + "@opentelemetry/instrumentation": "^0.221.0", "@opentelemetry/semantic-conventions": "^1.33.0" }, "engines": { @@ -866,12 +856,12 @@ } }, "node_modules/@opentelemetry/instrumentation-mysql": { - "version": "0.65.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql/-/instrumentation-mysql-0.65.0.tgz", - "integrity": "sha512-sh1wRjFaTt+8DOhJ0134rFtJoHVUXKI8faIWTbj4zZNw847dzUgmkO8xOluJ9Css0JzT4vCeZofJmq9mQmmESA==", + "version": "0.67.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql/-/instrumentation-mysql-0.67.0.tgz", + "integrity": "sha512-G4aRrVKcd2Aodqi7WzRZ3LQJNKrM8BpsXEVMHrOb9s8Lg0jZXNvlLY0NWL1yVJrjnqI/unwEsp2aVHkVImPMeA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/instrumentation": "^0.221.0", "@opentelemetry/semantic-conventions": "^1.33.0", "@types/mysql": "2.15.27" }, @@ -883,12 +873,12 @@ } }, "node_modules/@opentelemetry/instrumentation-mysql2": { - "version": "0.65.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql2/-/instrumentation-mysql2-0.65.0.tgz", - "integrity": "sha512-Om6BJ/bmFBzNkGbAzj/UV5sCKX6jCGzhTl1Gqgtim/O0dnPE7F2zN65u16Fq3JgyypGAwT2iwh13tYdWkc8/RA==", + "version": "0.67.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql2/-/instrumentation-mysql2-0.67.0.tgz", + "integrity": "sha512-AmviR7l0xMxhC83scY3u+NkkT6blhD/xK9tPi9nYtjNG1gwPtMgZjYOa3f9lGOwpXs/EwN7wiyAgxiO4KTcENA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/instrumentation": "^0.221.0", "@opentelemetry/semantic-conventions": "^1.33.0", "@opentelemetry/sql-common": "^0.42.0" }, @@ -900,12 +890,12 @@ } }, "node_modules/@opentelemetry/instrumentation-nestjs-core": { - "version": "0.65.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-nestjs-core/-/instrumentation-nestjs-core-0.65.0.tgz", - "integrity": "sha512-/q8fN2M2zGl+gQRaF79dzqvyvVqHAI11c7xAQZy9W1eAtQScNwaQtPm0EUo5+aOgakaTksBrsiHUF0rQS24NsQ==", + "version": "0.67.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-nestjs-core/-/instrumentation-nestjs-core-0.67.0.tgz", + "integrity": "sha512-lXb7pjobd2i/9Gmihf9wrOM0MgnDYBxOJq7uWEhZOqULodNFLyPCRUnWxSIbPQdUzlU36QtHjuAeaEBUJnqXkA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/instrumentation": "^0.221.0", "@opentelemetry/semantic-conventions": "^1.30.0" }, "engines": { @@ -916,12 +906,12 @@ } }, "node_modules/@opentelemetry/instrumentation-net": { - "version": "0.63.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-net/-/instrumentation-net-0.63.0.tgz", - "integrity": "sha512-fvmVdL4SlsYZf74mq6iLBPd6JJHRAe5utzN7Wt9e4nwa2S3pgExA9poOEmBL1CvIR47MceTA/A8vgVCF23ebbw==", + "version": "0.65.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-net/-/instrumentation-net-0.65.0.tgz", + "integrity": "sha512-W82H8UvaSrWynpI510CNJbq2Aq6L4/zuR/dAvoCZVYeRiChi0LHMI8i3rPe3Tmau2WBwE0jimaWgOs0GCfIHbQ==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/instrumentation": "^0.221.0", "@opentelemetry/semantic-conventions": "^1.33.0" }, "engines": { @@ -932,13 +922,13 @@ } }, "node_modules/@opentelemetry/instrumentation-openai": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-openai/-/instrumentation-openai-0.17.0.tgz", - "integrity": "sha512-X3aEZnzj7SJkn1nmqEoD9IljmqENnGrd0vcJngcEApT8uqhFaeimONqKeIzKYvUgC7k4OkAUxC7yfXzxIK1KlA==", + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-openai/-/instrumentation-openai-0.19.0.tgz", + "integrity": "sha512-zHz7m/aUMDyAap7UMzaaxDkbmxEyUOfMBfh+7KICNwTBmIOypMnuUWwXWAfIJLgl+2dnZlNzxZRknULeS4YIhg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api-logs": "^0.219.0", - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/api-logs": "^0.221.0", + "@opentelemetry/instrumentation": "^0.221.0", "@opentelemetry/semantic-conventions": "^1.36.0" }, "engines": { @@ -948,13 +938,25 @@ "@opentelemetry/api": "^1.3.0" } }, + "node_modules/@opentelemetry/instrumentation-openai/node_modules/@opentelemetry/api-logs": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.221.0.tgz", + "integrity": "sha512-OlanaW1vv7ufTqQ3/fPLI4arGt5ZoM+P8abOMki6uEYnpRazepSWDwDnnw+la7kE26SHVC18//SMccrDvLKOXQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/@opentelemetry/instrumentation-oracledb": { - "version": "0.44.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-oracledb/-/instrumentation-oracledb-0.44.0.tgz", - "integrity": "sha512-ncEfP4rzuZXBHJJtzWLYctK4Pq/FvZRiASxlFY9CJG9kGjaFTfzBUys0NiP27alYPvpjj0uDAMheDk9nihO8ZA==", + "version": "0.46.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-oracledb/-/instrumentation-oracledb-0.46.0.tgz", + "integrity": "sha512-nqxQvbp7HvsVPyDdgiZADPQX6B6ZUtLfm+XPuJHtQ3anxIcqU6qhJlrDIEM2LrZMShqf7bs84RHfDt2rgsp7hg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/instrumentation": "^0.221.0", "@opentelemetry/semantic-conventions": "^1.34.0", "@types/oracledb": "6.5.2" }, @@ -966,13 +968,13 @@ } }, "node_modules/@opentelemetry/instrumentation-pg": { - "version": "0.71.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.71.0.tgz", - "integrity": "sha512-jAhfyZeOkEKh3cQ5nm1tNWqHg7HFARyAe+p4BSoDHnB79c1woyEvDKqS11Hj/DjtceP+vrurIfcDs7Fqiy11mQ==", + "version": "0.73.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.73.0.tgz", + "integrity": "sha512-yf3tBVwLHB9cZNNPSToNrthx36ouPe4FctFxy7ya6vSJ6gaiKjNfA/IgFFeuBpZflEQhy6aesPqzZo8ZjFkvNg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/core": "^2.9.0", + "@opentelemetry/instrumentation": "^0.221.0", "@opentelemetry/semantic-conventions": "^1.34.0", "@opentelemetry/sql-common": "^0.42.0", "@types/pg": "8.15.6", @@ -986,14 +988,15 @@ } }, "node_modules/@opentelemetry/instrumentation-pino": { - "version": "0.65.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pino/-/instrumentation-pino-0.65.0.tgz", - "integrity": "sha512-p6eh+NRmzi1F+/4QG7XDqRk4ICdCNTsM5vdcwUPnpMie2MddgY1/ENmWvCF9r0Kh6QQRA2kkpnhoJFaiRQ5kVw==", + "version": "0.67.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pino/-/instrumentation-pino-0.67.0.tgz", + "integrity": "sha512-Hb6phi2x1bq23OIiesj4imQvXs9Y5MMLtpgXH9hOuMm9LpBYz2cxPNgpgZ/XATuRclP1eRPoSl399o5XKwfoIA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api-logs": "^0.219.0", - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.219.0" + "@opentelemetry/api-logs": "^0.221.0", + "@opentelemetry/core": "^2.9.0", + "@opentelemetry/instrumentation": "^0.221.0", + "@opentelemetry/semantic-conventions": "^1.41.1" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -1002,13 +1005,25 @@ "@opentelemetry/api": "^1.3.0" } }, + "node_modules/@opentelemetry/instrumentation-pino/node_modules/@opentelemetry/api-logs": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.221.0.tgz", + "integrity": "sha512-OlanaW1vv7ufTqQ3/fPLI4arGt5ZoM+P8abOMki6uEYnpRazepSWDwDnnw+la7kE26SHVC18//SMccrDvLKOXQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/@opentelemetry/instrumentation-redis": { - "version": "0.67.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-redis/-/instrumentation-redis-0.67.0.tgz", - "integrity": "sha512-TBjO4bPvfGH6bRjJJ+KrJhEqpHg3SWCFZ84MqsTWF639RQZmvkMhT2/DJsBAqqkq33IvHoDJysserqAfZN1s+Q==", + "version": "0.69.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-redis/-/instrumentation-redis-0.69.0.tgz", + "integrity": "sha512-lyCIEW89cYhMwaUSMBzsKHdwH2wOoqmuwXOARJneo9UL55govLIUCbYYAJ457oM8kdKADymlY4+SUW0DKQeIHw==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/instrumentation": "^0.221.0", "@opentelemetry/redis-common": "^0.38.3", "@opentelemetry/semantic-conventions": "^1.27.0" }, @@ -1020,13 +1035,13 @@ } }, "node_modules/@opentelemetry/instrumentation-restify": { - "version": "0.64.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-restify/-/instrumentation-restify-0.64.0.tgz", - "integrity": "sha512-X+gL4KpfPAx7Y07zKQVtyJPckhCcYJdSlEz0Kq0iR5nkQ8/AVWJ05/txl4voZbZdCuNdn+uLZTtJ60bAQwputQ==", + "version": "0.66.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-restify/-/instrumentation-restify-0.66.0.tgz", + "integrity": "sha512-9zbnL0ML2jFgJmmG1XPQTqwopCogC8eAtUQ0SXvYb+Ux2yuOBOvSg9XvRk/hcQf6WsRMll47cELCMC+IaI9I+g==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/core": "^2.9.0", + "@opentelemetry/instrumentation": "^0.221.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "engines": { @@ -1037,12 +1052,12 @@ } }, "node_modules/@opentelemetry/instrumentation-router": { - "version": "0.63.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-router/-/instrumentation-router-0.63.0.tgz", - "integrity": "sha512-zIpsZSHGvbaqiEazwUm1X0FkPnLXIwZcL/llu/UplkeGNU58bsw70l2uMqNqTb7J+tzsBC09CLVPty5BhW3X9Q==", + "version": "0.65.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-router/-/instrumentation-router-0.65.0.tgz", + "integrity": "sha512-ti9tDLFLhLoev5U/cGeQpFTlOjFFwrFbieZmwTFql9z8EijXDzFE7a3+3mgnxGl9CZR0luKCwFD/o51NPU+Ngg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/instrumentation": "^0.221.0", "@opentelemetry/semantic-conventions": "^1.27.0" }, "engines": { @@ -1053,14 +1068,14 @@ } }, "node_modules/@opentelemetry/instrumentation-runtime-node": { - "version": "0.32.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-runtime-node/-/instrumentation-runtime-node-0.32.0.tgz", - "integrity": "sha512-Jo1jSgrHlah3lPpGNPsIpF0q52D5uSLRJrztWUoPc1/Tli2ZWZ+cArgNtcdmiLuKhW21MwYbbcrNw1fbOPeR3A==", + "version": "0.34.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-runtime-node/-/instrumentation-runtime-node-0.34.0.tgz", + "integrity": "sha512-Yb3PcmuK/iIOWY49GSEcSGl7fR4r6UqhZnuy5EWvFaqKqqs9SYnQeyQUEAbnBnSougRpwFBDbdN1SSGcvMhbtQ==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api-logs": "^0.219.0", + "@opentelemetry/api-logs": "^0.221.0", "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.219.0" + "@opentelemetry/instrumentation": "^0.221.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -1069,13 +1084,25 @@ "@opentelemetry/api": "^1.3.0" } }, + "node_modules/@opentelemetry/instrumentation-runtime-node/node_modules/@opentelemetry/api-logs": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.221.0.tgz", + "integrity": "sha512-OlanaW1vv7ufTqQ3/fPLI4arGt5ZoM+P8abOMki6uEYnpRazepSWDwDnnw+la7kE26SHVC18//SMccrDvLKOXQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/@opentelemetry/instrumentation-socket.io": { - "version": "0.66.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-socket.io/-/instrumentation-socket.io-0.66.0.tgz", - "integrity": "sha512-XrZmLkFJktVLd3biQiP8BAhupRwPWLHGIiDCfyDAnWI6borIL0wD6BpwFKKPT/etpu4/5OaeAQ7qs/S+FY9nhQ==", + "version": "0.68.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-socket.io/-/instrumentation-socket.io-0.68.0.tgz", + "integrity": "sha512-Bhd0KApVBYV4WQMZZbKRYfvev7SudvCtSn6b36uyUbKowOMEoGpnVoIvm0rlrrBR0KmkcV3Y37SngCGUrTm3lg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.219.0" + "@opentelemetry/instrumentation": "^0.221.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -1085,12 +1112,12 @@ } }, "node_modules/@opentelemetry/instrumentation-tedious": { - "version": "0.38.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-tedious/-/instrumentation-tedious-0.38.0.tgz", - "integrity": "sha512-9sRWyIMBHDqJvxRVZ+eQ7jHJ9Iu+DapO27WLZbQF1nyD8xIvEMuDonQ/HnlQiaRdwnqFknXSQTFusJUT3mNVyQ==", + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-tedious/-/instrumentation-tedious-0.40.0.tgz", + "integrity": "sha512-zTNNxs+KUJf1J+lHzeTDxAIZdVJYvQ8mvGUfyiWcVFgVdl7+4XV+wOBMSd1tZcRRlopfcVODDCOVMx/N7+zvcA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/instrumentation": "^0.221.0", "@opentelemetry/semantic-conventions": "^1.33.0", "@types/tedious": "^4.0.14" }, @@ -1102,13 +1129,13 @@ } }, "node_modules/@opentelemetry/instrumentation-undici": { - "version": "0.29.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-undici/-/instrumentation-undici-0.29.0.tgz", - "integrity": "sha512-SnA+0XgGc595jtnwFVfWy7Vgfr5hle4D5YKIlm0U4z8aK9YoCZVUn1xAkVZ2evaJyykiDF50FBzr1XZ0uj8CPA==", + "version": "0.31.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-undici/-/instrumentation-undici-0.31.0.tgz", + "integrity": "sha512-qunCfgSFV+bjRdAYkWIjVX38jIN/Xj80CiERXXdzYAmdigCFvJPB3AY3j43bjJlkhgbJJSCGdbvQUSut8QIiyQ==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.219.0", + "@opentelemetry/core": "^2.9.0", + "@opentelemetry/instrumentation": "^0.221.0", "@opentelemetry/semantic-conventions": "^1.24.0" }, "engines": { @@ -1119,13 +1146,13 @@ } }, "node_modules/@opentelemetry/instrumentation-winston": { - "version": "0.63.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-winston/-/instrumentation-winston-0.63.0.tgz", - "integrity": "sha512-NFMHLYODph0rWGfT/QLv75hCsu1sxVAV79L8HduBCMo181jOTSRZHaqxfrvDtFOsvgYBaiUBa7Ga50w47wM3FA==", + "version": "0.65.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-winston/-/instrumentation-winston-0.65.0.tgz", + "integrity": "sha512-hWSPnS530deRa+ttzY+QiGmgsK7aQHpqxbQRm56yO1j4qnIXuYYHaoSJ8/4lLOEcXB+hZs0mAAJ6QiMl1aaiGA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api-logs": "^0.219.0", - "@opentelemetry/instrumentation": "^0.219.0" + "@opentelemetry/api-logs": "^0.221.0", + "@opentelemetry/instrumentation": "^0.221.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -1134,14 +1161,38 @@ "@opentelemetry/api": "^1.3.0" } }, + "node_modules/@opentelemetry/instrumentation-winston/node_modules/@opentelemetry/api-logs": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.221.0.tgz", + "integrity": "sha512-OlanaW1vv7ufTqQ3/fPLI4arGt5ZoM+P8abOMki6uEYnpRazepSWDwDnnw+la7kE26SHVC18//SMccrDvLKOXQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/instrumentation/node_modules/@opentelemetry/api-logs": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.221.0.tgz", + "integrity": "sha512-OlanaW1vv7ufTqQ3/fPLI4arGt5ZoM+P8abOMki6uEYnpRazepSWDwDnnw+la7kE26SHVC18//SMccrDvLKOXQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/@opentelemetry/otlp-exporter-base": { - "version": "0.219.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.219.0.tgz", - "integrity": "sha512-zvIxQX/AZUVKDU+hCuYx+7UkiP7GRdnk1ZbFQRYzHvYp47cAWR4j3IhoPhV9KaeXEv2xdGq3IA6PnpzDmLcmSA==", + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.221.0.tgz", + "integrity": "sha512-UFPIq80OH3Ns/oPFHRj14d4DTOxUo+MUFU8hUiCq5jTqFhdeJnfVSANHT+xp92409cA+oxzvlZCe6NM1wvCuBA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.8.0", - "@opentelemetry/otlp-transformer": "0.219.0" + "@opentelemetry/core": "2.10.0", + "@opentelemetry/otlp-transformer": "0.221.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -1151,15 +1202,15 @@ } }, "node_modules/@opentelemetry/otlp-grpc-exporter-base": { - "version": "0.219.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.219.0.tgz", - "integrity": "sha512-iIk/s8QQu39zpTrRRmsW/Eg3SE2+Hg8tLWepr2FLRgmwUpNd0IpCTLJEHJ77hpt4hgIS8MAh44UYI4xQPZwWlw==", + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.221.0.tgz", + "integrity": "sha512-rQDmNgyiGCTrescjnzH2ntVyUKVIq6I2UjuK8+stT/Xg0ZOT71FVJqwjFdspQl6Yol/Yqsut9bDo+ame8oTmDQ==", "license": "Apache-2.0", "dependencies": { "@grpc/grpc-js": "^1.14.3", - "@opentelemetry/core": "2.8.0", - "@opentelemetry/otlp-exporter-base": "0.219.0", - "@opentelemetry/otlp-transformer": "0.219.0" + "@opentelemetry/core": "2.10.0", + "@opentelemetry/otlp-exporter-base": "0.221.0", + "@opentelemetry/otlp-transformer": "0.221.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -1169,17 +1220,17 @@ } }, "node_modules/@opentelemetry/otlp-transformer": { - "version": "0.219.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.219.0.tgz", - "integrity": "sha512-aaYKAyXhw9VchKZVGOopD3Gw/kPsyrX2c6IQ0AW32mTjqmZOh5Y6Gf5OYqTNqVktAeBjmFinhyFaCwW6GYK9YQ==", + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.221.0.tgz", + "integrity": "sha512-lg6lkOU08Az23jVcn/0Els9HP+V8PnR4Km6p0KgpTggS0n/WuhnmY64rSh83Of9iR9nD+dpWr6adlcX8KzAwjg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api-logs": "0.219.0", - "@opentelemetry/core": "2.8.0", - "@opentelemetry/resources": "2.8.0", - "@opentelemetry/sdk-logs": "0.219.0", - "@opentelemetry/sdk-metrics": "2.8.0", - "@opentelemetry/sdk-trace-base": "2.8.0" + "@opentelemetry/api-logs": "0.221.0", + "@opentelemetry/core": "2.10.0", + "@opentelemetry/resources": "2.10.0", + "@opentelemetry/sdk-logs": "0.221.0", + "@opentelemetry/sdk-metrics": "2.10.0", + "@opentelemetry/sdk-trace": "2.10.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -1188,6 +1239,18 @@ "@opentelemetry/api": "^1.3.0" } }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/api-logs": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.221.0.tgz", + "integrity": "sha512-OlanaW1vv7ufTqQ3/fPLI4arGt5ZoM+P8abOMki6uEYnpRazepSWDwDnnw+la7kE26SHVC18//SMccrDvLKOXQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/@opentelemetry/propagator-aws-xray": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-aws-xray/-/propagator-aws-xray-2.2.0.tgz", @@ -1201,12 +1264,12 @@ } }, "node_modules/@opentelemetry/propagator-b3": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-2.8.0.tgz", - "integrity": "sha512-SazlvuSKi5533rPHTW2TwBwdMakhjZST4SYs0YauuvfGDkT13KbG1gJS75hV0uWVeevhtVP9sAIlaZLTHdSbMg==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-2.10.0.tgz", + "integrity": "sha512-GnA5B24H+1w8BO21J0q+IWNB0z1v+AGbcquTdIt/dufibhnhgxaA8YKvz0I3akRZhB1jHT+/tlzK+qlAjEDybQ==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.8.0" + "@opentelemetry/core": "2.10.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -1216,12 +1279,12 @@ } }, "node_modules/@opentelemetry/propagator-jaeger": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-2.8.0.tgz", - "integrity": "sha512-Xnz9zZvvQzUw+9DrOn0MomR7BxFCkA2pcfXBQuHC28ndJpSbjLs7knzYb05kw5SyCjSsEWombkZMgGcJSk8JVg==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-2.10.0.tgz", + "integrity": "sha512-yw/IX8DL470dSMZJoE82ScfYGp7JWZ/G8kFJo35ZILUVTB2jFPTOaioN+8s09pH0RHsWNhweVZb+ZnjJJpCChg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.8.0" + "@opentelemetry/core": "2.10.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -1240,9 +1303,9 @@ } }, "node_modules/@opentelemetry/resource-detector-alibaba-cloud": { - "version": "0.34.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-alibaba-cloud/-/resource-detector-alibaba-cloud-0.34.0.tgz", - "integrity": "sha512-hUs4CK7MbRfffw8y5zR4Mo37MJRR3Zt8Ub4rgMkIk7gL8jozjV7k+zwIk9grz5kGAuD406BDDIghaY8090K4Zw==", + "version": "0.36.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-alibaba-cloud/-/resource-detector-alibaba-cloud-0.36.0.tgz", + "integrity": "sha512-s75zJV1ShpYL5nk2cODfZY05Haw2hGxcfEFMu3ymvh2QU3HrhXaCW+rmNkhXhRrO8YophMFTyVdb7iCDleC/JQ==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", @@ -1256,9 +1319,9 @@ } }, "node_modules/@opentelemetry/resource-detector-aws": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-aws/-/resource-detector-aws-2.19.0.tgz", - "integrity": "sha512-ELKQCtbc7g2ghbteLftKzXvI3MkIfENrRjAaYd2h9cWNj8g/Dx3oDzpxfcv9mBigtwwWCZwQRr8R//uxREpdrg==", + "version": "2.21.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-aws/-/resource-detector-aws-2.21.0.tgz", + "integrity": "sha512-Veavy+khoywR+Hv065SU5jucFTGTiW1KXo39CsJ+8wqdYYz8jiRJPnQ20Kd+X9HbV2+Abb0l5CrJIdxK1ZOqBg==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", @@ -1273,9 +1336,9 @@ } }, "node_modules/@opentelemetry/resource-detector-azure": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-azure/-/resource-detector-azure-0.27.0.tgz", - "integrity": "sha512-m6HCEmK12QpEcWbKtvGpQtoDVceXtpB14AaaW/t5f6gHeLuGq1QivkuohkvKMkxgAdwIHxEQ8qof3whWBpd8JA==", + "version": "0.29.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-azure/-/resource-detector-azure-0.29.0.tgz", + "integrity": "sha512-lWm0vjjlQMoc4Xvvd+dW/OZWT/SI4w+cIN7kbm8KimIZCr1EpAyvyQ7WEOrGoBoXCpQCrsZ18uKTooDgiBCGIw==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", @@ -1290,9 +1353,9 @@ } }, "node_modules/@opentelemetry/resource-detector-container": { - "version": "0.8.10", - "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-container/-/resource-detector-container-0.8.10.tgz", - "integrity": "sha512-aMU2wG4ktcqy6zEotdbIkkX5ACKRxEZLX1ZlMH0dea0M2+2KfabJc1lHPqAIFaa+CD37fCION55e69QmYwqX4A==", + "version": "0.8.12", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-container/-/resource-detector-container-0.8.12.tgz", + "integrity": "sha512-EJRFfIY26whY0w5RDxMRXlfBDgDS001JYMHuOVuDBBsRrV4MBqoVajR9B0L9Vy728+w/HNVnSQkpJFacFr+klg==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", @@ -1306,9 +1369,9 @@ } }, "node_modules/@opentelemetry/resource-detector-gcp": { - "version": "0.54.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-gcp/-/resource-detector-gcp-0.54.0.tgz", - "integrity": "sha512-u+6sBzQO03QQGFhxjzFa7uNbH6iQpWTcrpWyomxuppH3AN/+1mm3DRVseS1CiRq9VBKrFO0UosWAdD7fWVUrrg==", + "version": "0.56.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resource-detector-gcp/-/resource-detector-gcp-0.56.0.tgz", + "integrity": "sha512-H8yNeqTsuapbXs6MLZTtelfUCk+5D8jD3+KosCJaXOyx5gl3EWWvs70HbNXTUO4VYLxccySFYJYVCd8YMM0NJw==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/core": "^2.0.0", @@ -1323,12 +1386,12 @@ } }, "node_modules/@opentelemetry/resources": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.8.0.tgz", - "integrity": "sha512-qmXQ27ilDbUK/vGMqwL8D4/rhn76C+sherM4wTbjlfknR8Nvfc/hCxjRJPhkzZzUsPiNg16SA31NxMabwttRjg==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.10.0.tgz", + "integrity": "sha512-q6MMm2zhggzsHVNbabYwut+a6nbuQQe3URUoxaojM/8K1IBfwwPzvxIjNi2/lI1TFe+fMHMW9MWhrtDLEXEnkA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.8.0", + "@opentelemetry/core": "2.10.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { @@ -1339,14 +1402,14 @@ } }, "node_modules/@opentelemetry/sdk-logs": { - "version": "0.219.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.219.0.tgz", - "integrity": "sha512-s6lTKRakaPClvKoWHRChxnXjDMkM/TQ30ff78jN6EBGf7MI7VzANE5PU3f4z9qDUudWjvZjOLHG0rBnBKYvoXA==", + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.221.0.tgz", + "integrity": "sha512-FaDcazjyMp7TZZZAsqbo4IkovP0UegoCu0EBkiNt+qCqvUf7FPAsfcrZ3+ZEkKgXZ/jHafop+JoGPDk3A0SmLg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api-logs": "0.219.0", - "@opentelemetry/core": "2.8.0", - "@opentelemetry/resources": "2.8.0", + "@opentelemetry/api-logs": "0.221.0", + "@opentelemetry/core": "2.10.0", + "@opentelemetry/resources": "2.10.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { @@ -1356,14 +1419,26 @@ "@opentelemetry/api": ">=1.4.0 <1.10.0" } }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/api-logs": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.221.0.tgz", + "integrity": "sha512-OlanaW1vv7ufTqQ3/fPLI4arGt5ZoM+P8abOMki6uEYnpRazepSWDwDnnw+la7kE26SHVC18//SMccrDvLKOXQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/@opentelemetry/sdk-metrics": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.8.0.tgz", - "integrity": "sha512-UDBGaj6W0Rgy5rTTaoxs8gVGF/aGkAKyjurJv7se6wjRxJu7FoquTLT/vt54DZfo4crbprYfhX/SOK9+BPw1qg==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.10.0.tgz", + "integrity": "sha512-t6r1VSvXNtSDnPXU1FbZeetJb7yyovHmgu0wRSoftxtE0g2rSNhQZQUy69sRUCL+iioJpX8SN/S6wq6ZtvLySQ==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.8.0", - "@opentelemetry/resources": "2.8.0" + "@opentelemetry/core": "2.10.0", + "@opentelemetry/resources": "2.10.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -1373,36 +1448,66 @@ } }, "node_modules/@opentelemetry/sdk-node": { - "version": "0.219.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-node/-/sdk-node-0.219.0.tgz", - "integrity": "sha512-NWLpWLEb8gV3+JBHYoIrktbM385wyHpRJoh3J/4Q52d4PR+AlPMNGJT3DzBUrDSUEVbKAXoHR+EDAPxtiNcj8g==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.219.0", - "@opentelemetry/configuration": "0.219.0", - "@opentelemetry/context-async-hooks": "2.8.0", - "@opentelemetry/core": "2.8.0", - "@opentelemetry/exporter-logs-otlp-grpc": "0.219.0", - "@opentelemetry/exporter-logs-otlp-http": "0.219.0", - "@opentelemetry/exporter-logs-otlp-proto": "0.219.0", - "@opentelemetry/exporter-metrics-otlp-grpc": "0.219.0", - "@opentelemetry/exporter-metrics-otlp-http": "0.219.0", - "@opentelemetry/exporter-metrics-otlp-proto": "0.219.0", - "@opentelemetry/exporter-prometheus": "0.219.0", - "@opentelemetry/exporter-trace-otlp-grpc": "0.219.0", - "@opentelemetry/exporter-trace-otlp-http": "0.219.0", - "@opentelemetry/exporter-trace-otlp-proto": "0.219.0", - "@opentelemetry/exporter-zipkin": "2.8.0", - "@opentelemetry/instrumentation": "0.219.0", - "@opentelemetry/otlp-exporter-base": "0.219.0", - "@opentelemetry/otlp-grpc-exporter-base": "0.219.0", - "@opentelemetry/propagator-b3": "2.8.0", - "@opentelemetry/propagator-jaeger": "2.8.0", - "@opentelemetry/resources": "2.8.0", - "@opentelemetry/sdk-logs": "0.219.0", - "@opentelemetry/sdk-metrics": "2.8.0", - "@opentelemetry/sdk-trace-base": "2.8.0", - "@opentelemetry/sdk-trace-node": "2.8.0", + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-node/-/sdk-node-0.221.0.tgz", + "integrity": "sha512-UbYuvtBrQQB5Prsh9KOKy4kxzexFxfMs5MkteHeWMoswsEB7kiNhyUVkAOFW/qsEzNHtrkgyghrD2ilZJa+5YA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.221.0", + "@opentelemetry/configuration": "0.221.0", + "@opentelemetry/context-async-hooks": "2.10.0", + "@opentelemetry/core": "2.10.0", + "@opentelemetry/exporter-logs-otlp-grpc": "0.221.0", + "@opentelemetry/exporter-logs-otlp-http": "0.221.0", + "@opentelemetry/exporter-logs-otlp-proto": "0.221.0", + "@opentelemetry/exporter-metrics-otlp-grpc": "0.221.0", + "@opentelemetry/exporter-metrics-otlp-http": "0.221.0", + "@opentelemetry/exporter-metrics-otlp-proto": "0.221.0", + "@opentelemetry/exporter-prometheus": "0.221.0", + "@opentelemetry/exporter-trace-otlp-grpc": "0.221.0", + "@opentelemetry/exporter-trace-otlp-http": "0.221.0", + "@opentelemetry/exporter-trace-otlp-proto": "0.221.0", + "@opentelemetry/exporter-zipkin": "2.10.0", + "@opentelemetry/instrumentation": "0.221.0", + "@opentelemetry/otlp-exporter-base": "0.221.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.221.0", + "@opentelemetry/propagator-b3": "2.10.0", + "@opentelemetry/propagator-jaeger": "2.10.0", + "@opentelemetry/resources": "2.10.0", + "@opentelemetry/sdk-logs": "0.221.0", + "@opentelemetry/sdk-metrics": "2.10.0", + "@opentelemetry/sdk-trace": "2.10.0", + "@opentelemetry/sdk-trace-base": "2.10.0", + "@opentelemetry/sdk-trace-node": "2.10.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-node/node_modules/@opentelemetry/api-logs": { + "version": "0.221.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.221.0.tgz", + "integrity": "sha512-OlanaW1vv7ufTqQ3/fPLI4arGt5ZoM+P8abOMki6uEYnpRazepSWDwDnnw+la7kE26SHVC18//SMccrDvLKOXQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/sdk-trace": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace/-/sdk-trace-2.10.0.tgz", + "integrity": "sha512-MfQGq3GRmTh5fM/y+OjaO0vj6+luCB1XO2gfXCalKCfgKw0eHL++sm75DNweC6ohlp+aFvACqeE0fYayqdRaoQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.10.0", + "@opentelemetry/resources": "2.10.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { @@ -1413,13 +1518,14 @@ } }, "node_modules/@opentelemetry/sdk-trace-base": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.8.0.tgz", - "integrity": "sha512-mhU4jp+vW0mGbFRd+GeXHvmfA4aDqWjBjLC3pE5XMpLs0IE2ryYb019Ts2AQrOq67gaTF25D91+fgvEHDZEnuQ==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.10.0.tgz", + "integrity": "sha512-GuYQQT7QD2EeO8lcZLRQzcbOyhqAzL+6WWTKTU9mSUBYBazkEDl+VrQcXQhbB08OWM9anD1aHleVadzulpOaUQ==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.8.0", - "@opentelemetry/resources": "2.8.0", + "@opentelemetry/core": "2.10.0", + "@opentelemetry/resources": "2.10.0", + "@opentelemetry/sdk-trace": "2.10.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { @@ -1430,14 +1536,14 @@ } }, "node_modules/@opentelemetry/sdk-trace-node": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-2.8.0.tgz", - "integrity": "sha512-nZt9OGufioAc3AfoLTqA9bsAeaMJAictYDdI2VcNQ+PmT+3rfKjAZDZvgPfd8VPX0O5Bw1hdQF6kDK8VSpZiWg==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-2.10.0.tgz", + "integrity": "sha512-GZK/G6oZyBLGlH1pUgeDch7D91KoHd2uotUGIkWCPi9GI5T9X0p4L7nNAMDR1BQjkRYoDqo+ddfVx9t5Uhys+Q==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/context-async-hooks": "2.8.0", - "@opentelemetry/core": "2.8.0", - "@opentelemetry/sdk-trace-base": "2.8.0" + "@opentelemetry/context-async-hooks": "2.10.0", + "@opentelemetry/core": "2.10.0", + "@opentelemetry/sdk-trace-base": "2.10.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -1536,9 +1642,9 @@ } }, "node_modules/@types/node": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-26.0.0.tgz", - "integrity": "sha512-vf2YFi1iY9lHGwNJMs01biZFbKJkrZR1T6/MlzjhJLPdntOHLhTrDSnSVcdtvjihi4VQNlrFRIxLsDBlQpAipA==", + "version": "26.1.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.1.tgz", + "integrity": "sha512-nxAkRSVkN1Y0JC1W8ky/fTfkGsMmcrRsbx+3XoZE+rMOX71kLYTV7fLXpqud1GpbpP5TuffXFqfX7fH2GgZREw==", "license": "MIT", "dependencies": { "undici-types": "~8.3.0" @@ -1588,27 +1694,6 @@ "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", "license": "MIT" }, - "node_modules/acorn": { - "version": "8.17.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz", - "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==", - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-import-attributes": { - "version": "1.9.5", - "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", - "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", - "license": "MIT", - "peerDependencies": { - "acorn": "^8" - } - }, "node_modules/agent-base": { "version": "7.1.4", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", @@ -1658,9 +1743,9 @@ } }, "node_modules/brace-expansion": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", - "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.2.tgz", + "integrity": "sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -1829,6 +1914,12 @@ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "license": "MIT" }, + "node_modules/es-module-lexer": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.1.tgz", + "integrity": "sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==", + "license": "MIT" + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -1923,9 +2014,9 @@ } }, "node_modules/gcp-metadata": { - "version": "8.1.3", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-8.1.3.tgz", - "integrity": "sha512-ziTrzUhhpL9Zk5k0HHzgP/KIpWDJT0VMBC/ynt/QIBvTW+UUcSivQRl6VlwTf/EilDxtSWklHoRsKy1c4k+59w==", + "version": "8.1.4", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-8.1.4.tgz", + "integrity": "sha512-iJ9KMsiu+xKtNRX0PmGLSaIU3bUBAyzWTyqKemKPzNPsmmsBCQYmlNg+brEbES7IHSXtdVwzBPzx1vz3FAaipw==", "license": "Apache-2.0", "dependencies": { "gaxios": "7.1.3", @@ -1989,14 +2080,13 @@ } }, "node_modules/import-in-the-middle": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-3.1.0.tgz", - "integrity": "sha512-c0AeAV8VcwZzfYE7euTZY3H+VXUPMVugiovdosq80lqEXJmOekg3zGUAYg6KImHMaMuBoTUfTv7xNpUFdy0hJA==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-3.3.2.tgz", + "integrity": "sha512-jTd2FfOgOWOdgjkHuk/1Ms8VKFXkPs15ymYBETw1sAOrO/dY3XeGVRWir9qBbw7pXr0T2eTFwfCZ+N02HmiNGA==", "license": "Apache-2.0", "dependencies": { - "acorn": "^8.15.0", - "acorn-import-attributes": "^1.9.5", "cjs-module-lexer": "^2.2.0", + "es-module-lexer": "^2.2.0", "module-details-from-path": "^1.0.4" }, "engines": { @@ -2259,9 +2349,9 @@ } }, "node_modules/protobufjs": { - "version": "8.6.4", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-8.6.4.tgz", - "integrity": "sha512-/+XMv9JalknuncEJSwsyEVlwcxVLKx2iaoSUXFZA86MJkdqyOdfrlB1sB7S6aKyUk9tl20YY+SgQe5J2sJHTcg==", + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-8.7.1.tgz", + "integrity": "sha512-agdGHrXNTv0IrYscJPDou/PlEJk1c/hBZ9o/B5NH2i/nSPtPqacNxzgwf1CebXxFMjMrZH5sqv9uQuw96aGt/A==", "license": "BSD-3-Clause", "dependencies": { "long": "^5.3.2" @@ -2489,9 +2579,9 @@ } }, "node_modules/systeminformation": { - "version": "5.31.7", - "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.31.7.tgz", - "integrity": "sha512-/8NC53e5nP9nmhn42/ncdOkyJnOoue/Vy+tJOyUGd1Yv66G069wK4rrziwhrqDETgk78CudTQupw5z19S5uoZw==", + "version": "5.33.1", + "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.33.1.tgz", + "integrity": "sha512-DEN6ICHk3Tk0Uf/hrAHh7xlt7iL5CJFBtPZinA0H62DrGG/KPKqq/Nzj6lCXPS4Ay/sf/14zNnk9LpqKzBIc+w==", "license": "MIT", "os": [ "darwin", @@ -2507,7 +2597,7 @@ "systeminformation": "lib/cli.js" }, "engines": { - "node": ">=8.0.0" + "node": ">=10.0.0" }, "funding": { "type": "Buy me a coffee", diff --git a/nodejs-instrumentation/package.json b/nodejs-instrumentation/package.json index 217bc96c..21b78a58 100644 --- a/nodejs-instrumentation/package.json +++ b/nodejs-instrumentation/package.json @@ -11,15 +11,15 @@ "license": "ISC", "dependencies": { "@opentelemetry/api": "^1.9.0", - "@opentelemetry/auto-instrumentations-node": "^0.77.0", - "@opentelemetry/exporter-metrics-otlp-grpc": "^0.219.0", - "@opentelemetry/exporter-trace-otlp-grpc": "^0.219.0", + "@opentelemetry/auto-instrumentations-node": "^0.79.0", + "@opentelemetry/exporter-metrics-otlp-grpc": "^0.221.0", + "@opentelemetry/exporter-trace-otlp-grpc": "^0.221.0", "@opentelemetry/propagator-aws-xray": "^2.2.0", "@opentelemetry/resource-detector-aws": "^2.19.0", "@opentelemetry/resource-detector-container": "^0.8.10", "@opentelemetry/resources": "^2.8.0", "@opentelemetry/sdk-metrics": "^2.8.0", - "@opentelemetry/sdk-node": "^0.219.0", + "@opentelemetry/sdk-node": "^0.221.0", "@opentelemetry/sdk-trace-base": "^2.8.0", "@opentelemetry/semantic-conventions": "^1.41.1", "@opentelemetry/winston-transport": "^0.29.0" From 6596a7399cc344cfa09ce67e7992c2cacaa179c8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Jul 2026 15:12:06 +0100 Subject: [PATCH 24/57] build(deps): bump axios (#926) Bumps the server-prod-security group with 1 update in the /server directory: [axios](https://github.com/axios/axios). Updates `axios` from 1.16.0 to 1.18.1 - [Release notes](https://github.com/axios/axios/releases) - [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md) - [Commits](https://github.com/axios/axios/compare/v1.16.0...v1.18.1) --- updated-dependencies: - dependency-name: axios dependency-version: 1.18.1 dependency-type: indirect dependency-group: server-prod-security ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- server/package-lock.json | 32 +++++++++++++++++++++++++++++--- server/package.json | 2 +- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/server/package-lock.json b/server/package-lock.json index 77d2fac8..4e3c10f6 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -13050,16 +13050,42 @@ } }, "node_modules/axios": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.16.0.tgz", - "integrity": "sha512-6hp5CwvTPlN2A31g5dxnwAX0orzM7pmCRDLnZSX772mv8WDqICwFjowHuPs04Mc8deIld1+ejhtaMn5vp6b+1w==", + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.18.1.tgz", + "integrity": "sha512-3nTvFlvpn9Zu/RkHUqtc7/+al4UpRW5az71ap5zccp6e8RAYEzhMTecX8Dz1wWDYrPpUoB1HAQEGEAEvUr7S9g==", "license": "MIT", "dependencies": { "follow-redirects": "^1.16.0", "form-data": "^4.0.5", + "https-proxy-agent": "^5.0.1", "proxy-from-env": "^2.1.0" } }, + "node_modules/axios/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/axios/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/babel-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", diff --git a/server/package.json b/server/package.json index cd0d3303..4db8f40e 100644 --- a/server/package.json +++ b/server/package.json @@ -162,7 +162,7 @@ }, "uuid": "^14.0.0", "fast-xml-parser": "^5.8.0", - "axios": "^1.16.0", + "axios": "^1.18.1", "fast-uri": "^3.1.2", "protobufjs": "^7.6.3", "@protobufjs/utf8": "^1.1.1" From 43843e2dd07e14708bfe8e0ab2ee6e9992b5e76f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Jul 2026 15:13:22 +0100 Subject: [PATCH 25/57] build(deps-dev): bump knip in /db in the db-dev group across 1 directory (#882) Bumps the db-dev group with 1 update in the /db directory: [knip](https://github.com/webpro-nl/knip/tree/HEAD/packages/knip). Updates `knip` from 6.16.1 to 6.26.0 - [Release notes](https://github.com/webpro-nl/knip/releases) - [Commits](https://github.com/webpro-nl/knip/commits/knip@6.26.0/packages/knip) --- updated-dependencies: - dependency-name: knip dependency-version: 6.21.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: db-dev ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- db/package-lock.json | 471 +++++++++++++++++++++++++------------------ db/package.json | 2 +- 2 files changed, 272 insertions(+), 201 deletions(-) diff --git a/db/package-lock.json b/db/package-lock.json index 9ccd5d3c..b2f54e21 100644 --- a/db/package-lock.json +++ b/db/package-lock.json @@ -21,7 +21,7 @@ "devDependencies": { "@types/pg": "^8.10.2", "dotenv": "^17.4.2", - "knip": "^6.16.1", + "knip": "^6.26.0", "typescript": "^6.0.3" } }, @@ -56,21 +56,21 @@ } }, "node_modules/@emnapi/core": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", - "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", + "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", "dev": true, "license": "MIT", "optional": true, "dependencies": { - "@emnapi/wasi-threads": "1.2.1", + "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" } }, "node_modules/@emnapi/runtime": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", - "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", + "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", "dev": true, "license": "MIT", "optional": true, @@ -79,9 +79,9 @@ } }, "node_modules/@emnapi/wasi-threads": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", - "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", + "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", "dev": true, "license": "MIT", "optional": true, @@ -115,14 +115,14 @@ } }, "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", - "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", + "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==", "dev": true, "license": "MIT", "optional": true, "dependencies": { - "@tybys/wasm-util": "^0.10.1" + "@tybys/wasm-util": "^0.10.3" }, "funding": { "type": "github", @@ -169,9 +169,9 @@ } }, "node_modules/@oxc-parser/binding-android-arm-eabi": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm-eabi/-/binding-android-arm-eabi-0.133.0.tgz", - "integrity": "sha512-l/44caGse+VpnY9gx0yvvc5QnnG3yG1FO3KZgYvNL1GZrfK86zIwAOgGEVlxDyRymzrU/KHiblPFpevKOmJmUA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm-eabi/-/binding-android-arm-eabi-0.137.0.tgz", + "integrity": "sha512-KDs+0VPdEmasOkpuJHW9V5WCF+cvYdMQv2Jd+aJXt+cxIx12NToRQRbXaRwUEDsZw+/jMk81Ve8ZFbjUkJTOwA==", "cpu": [ "arm" ], @@ -186,9 +186,9 @@ } }, "node_modules/@oxc-parser/binding-android-arm64": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm64/-/binding-android-arm64-0.133.0.tgz", - "integrity": "sha512-KUHmPMziLBp4u+zbrLdB7iWS7KshuZe+RAp7ELnY9SI9nNXBZ+dp8fiBqWOxhXqn+FQg3a4UcQhwmsJOKV8Jjg==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm64/-/binding-android-arm64-0.137.0.tgz", + "integrity": "sha512-WhALNzfy3x/RfC6bsqX+csavuUY0yHHE7XfgPE5M542uhoBZUUoGTPG+nkMbGoG4+gcfss5s7urMyn5QBHu0sw==", "cpu": [ "arm64" ], @@ -203,9 +203,9 @@ } }, "node_modules/@oxc-parser/binding-darwin-arm64": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.133.0.tgz", - "integrity": "sha512-q8dWmnU/8ea2tga9w2f1PinQ5rcMPDUGkF64T189b65YMjUomET4oy5oRldOr4AwOQkneOG/Zttnz1Dvrc62wg==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.137.0.tgz", + "integrity": "sha512-bFPr5hgmNMOMoyPTGtdsK4Ug21RovIPojRMgDDhSp1LtCnc/DkLwGONKjgRjszg677RlGnkYSviQ8hHaUPOVYA==", "cpu": [ "arm64" ], @@ -220,9 +220,9 @@ } }, "node_modules/@oxc-parser/binding-darwin-x64": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.133.0.tgz", - "integrity": "sha512-cOKeIELIB2bJnCKwqx4Rdj+1Lss/U6uCbLxRySZrhyOOQa1flKhwZFjEHRHxk8fU1NKmhK5OnTdPQ4CpjuFuVw==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.137.0.tgz", + "integrity": "sha512-CL5dMm1asqXIDZHg14FLxj3Mc36w8PI7xCWh1uA4is6z8g2XrIILoTcQYOxDbwzuk34RDPX5IAGUxZr6LA9KAg==", "cpu": [ "x64" ], @@ -237,9 +237,9 @@ } }, "node_modules/@oxc-parser/binding-freebsd-x64": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.133.0.tgz", - "integrity": "sha512-OpaSv4pW3KgFrMYQxTaS0aOE4T1DQF3qZE/4B6uqqv1KgPWWd4UQhJALi8PJPX1RRV5K7ThKXRfF7qGg2+3l1A==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.137.0.tgz", + "integrity": "sha512-79h8rYGnSlKPGWo7mHr2ixO6ea7aW8B0CT965SZ8SLbNnCOH5aOYBTeVXUY6eMvEaiLyWr8Skuiugr5pDYgLGw==", "cpu": [ "x64" ], @@ -254,9 +254,9 @@ } }, "node_modules/@oxc-parser/binding-linux-arm-gnueabihf": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.133.0.tgz", - "integrity": "sha512-JGK1wlGrGwxBIlVSF7KWTX1/ru6BEtf28fRROztDRkLfiW+Kxa4onnriezMIiogfn9hVw2KzYcKiLjkLR2ns8A==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.137.0.tgz", + "integrity": "sha512-ASgmlSimhGyr0lksgVIo6hibz1obnDq4qJbiMX/AzltfgPnanRrzG1Q+23g8ljOHOjv6dsznkUuCYL3gg0sY1Q==", "cpu": [ "arm" ], @@ -271,9 +271,9 @@ } }, "node_modules/@oxc-parser/binding-linux-arm-musleabihf": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.133.0.tgz", - "integrity": "sha512-yuZO533Ftonxn/iyoqQzURzLQHMspvsIyfiCSNi1t/ER4eIQaR0SsmUOUm5b/lmSig7IWIUa5/BrbEkAPwcilQ==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.137.0.tgz", + "integrity": "sha512-AU2J9aa22Sx32wRGnDjybOU9TQXXQUud5sdUi+ZB0XxwM8aToWLweV+yA0wlQm0yIUVqljquqoHCYEq9II8gJQ==", "cpu": [ "arm" ], @@ -288,13 +288,16 @@ } }, "node_modules/@oxc-parser/binding-linux-arm64-gnu": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.133.0.tgz", - "integrity": "sha512-hvpbqT5pN2rR+3+xtWeizwfR/aZ0vGceg6TqYMl+ToxMpk9/tmnX7kSvQnfEUkoua8mhogzvIKsAkn0wxgblBA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.137.0.tgz", + "integrity": "sha512-GdEtiG89yMr7XkUGxifgodXEEm2f+xW2f9CpDjlgAnBOwhTmrpQMvhOGobLVKUyzf/qHBXW16smk5zbF3nZU6w==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -305,13 +308,16 @@ } }, "node_modules/@oxc-parser/binding-linux-arm64-musl": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.133.0.tgz", - "integrity": "sha512-wJQGamIosQBoJHW9+S5XxrtKRo3eyJxsnS1XCPrqN0LHi8uw1pTqqTfn3t/NVuvbBg7Pumn4ez9Eidgcn0xbEg==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.137.0.tgz", + "integrity": "sha512-EGJ+Bs8iXx8KBH8DQ5BLoEm5lnHaYjlh4/8j8vFhrr/6z4tqONy5BZDzLpKmmNWlN6Hlc5r8YOuBVHqZ9vRFEQ==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -322,13 +328,16 @@ } }, "node_modules/@oxc-parser/binding-linux-ppc64-gnu": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.133.0.tgz", - "integrity": "sha512-Koaz32/O5+abIfrNGdyndgRvdOZ9jEf5/z3Ep9h3h2QWpdDiUQpVwgH0OcMXCs+l9aXxPLtkupqyVig9W6FDKw==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.137.0.tgz", + "integrity": "sha512-vzFUQENy/fnbSe5DZWovq6tIBc1uhuMztanSW6rz1e9WdQE4gHwYuD7ZII6JnrJifd1R3RSoqiZbgRFlVL2tYQ==", "cpu": [ "ppc64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -339,13 +348,16 @@ } }, "node_modules/@oxc-parser/binding-linux-riscv64-gnu": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.133.0.tgz", - "integrity": "sha512-R4vOjWzxhnNWHnVLeiB6jNuIifdy9vcMXZGPc7StXcxBovI+U2zg1QhZ9o8OjV80oGivs1lX5NfPLzk4IPqlRA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.137.0.tgz", + "integrity": "sha512-SfVI14HBQs9gtLcUD5hTt5hsNbdrqSUNg9S8muN+LhVQ5nf1WwH3hAoK6B9NKgdYgWAQSXFXGiiBedQ4r/BKuw==", "cpu": [ "riscv64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -356,13 +368,16 @@ } }, "node_modules/@oxc-parser/binding-linux-riscv64-musl": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.133.0.tgz", - "integrity": "sha512-iwgBNUTHiMdxARLYuM0SBlnYeb19iw1Ea5M+4ERZupCsBMLArti6FyZ6UfFjJxIiTDr2oW2DGQFxlQVQ/dW9rA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.137.0.tgz", + "integrity": "sha512-e7Ppy4FCIFNQxT/ikSeIWFoQ0l+N9vgtRBtLcyZXeolTzApyVoPqEXsYPrcdM/9i0Bwk8knvYd37vaEMxHyi6g==", "cpu": [ "riscv64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -373,13 +388,16 @@ } }, "node_modules/@oxc-parser/binding-linux-s390x-gnu": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.133.0.tgz", - "integrity": "sha512-ZwZNo8FZmB/gVfboQl+wXilBigGl+6nQQs+nITOeAP/HcAOjiHl6XZJL9F/KXNEspODQcbjAiyjUbeCJd9a0fA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.137.0.tgz", + "integrity": "sha512-Bho5qFwdhqsIFR7gipYEUlqvi3SRrY8sugxXig380MIaakBB1PyU9+7dBiBVScfImTNWhijUxdBwqrprGdq5WA==", "cpu": [ "s390x" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -390,13 +408,16 @@ } }, "node_modules/@oxc-parser/binding-linux-x64-gnu": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.133.0.tgz", - "integrity": "sha512-govCvWx1dBlED3uu4qXctxpRcouu9I8Kn+DBktGCl760JtlGJzc9l/OmPJKlYWSbrRqKkMZehNeZ/4Wfma7uSA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.137.0.tgz", + "integrity": "sha512-36mGWtg7PyFzjJwGDkH6/F4o2nIDEoKXLPr/X/lwqklkomQwJJt1I5GJVmGhovUEmgPK5WAeAZMqlFCehwiy9Q==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -407,13 +428,16 @@ } }, "node_modules/@oxc-parser/binding-linux-x64-musl": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.133.0.tgz", - "integrity": "sha512-ssTlpXD5Mq9uCssDJPzlRWqBt4Y7Zzd9i+XZhWmK/9Y6KUIuAxVYTYiI8lxcGWi0+3/Cz4A8q9UrD4NK9Y2j7g==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.137.0.tgz", + "integrity": "sha512-/Jqx6+N7A44n2BdvUr7pXhVr2vFjs6WGH3unZRczwrfiH0H1zY0QwKQMG/dtRiTlKGDKGukznPT8lx84/oEsZg==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -424,9 +448,9 @@ } }, "node_modules/@oxc-parser/binding-openharmony-arm64": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-openharmony-arm64/-/binding-openharmony-arm64-0.133.0.tgz", - "integrity": "sha512-51aByfXhPtLEdWG4a2Ihdw6cPWV1ei1AarALpFdDP8MLWDLE2NuUMgbo3DERR2Kt8fT/ok1GUvBiLxVGke9uUQ==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-openharmony-arm64/-/binding-openharmony-arm64-0.137.0.tgz", + "integrity": "sha512-9Uj0qHNNl+OgT1UTGwF7ixIXU6T1u2SbMidmgPy/h1h/fl2gRS6YpAxxY1gwHofcWjoTwkoMFd8xs5Vuj6GOFA==", "cpu": [ "arm64" ], @@ -441,9 +465,9 @@ } }, "node_modules/@oxc-parser/binding-wasm32-wasi": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-wasm32-wasi/-/binding-wasm32-wasi-0.133.0.tgz", - "integrity": "sha512-2e16tkKp+wDO2GTAmXfxbBcCmGEaFPIJEIRBBmVKNVXSc8/fJsSIaBGyFTPHM9ST5GNWgJcYIt94rDTks+PLwA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-wasm32-wasi/-/binding-wasm32-wasi-0.137.0.tgz", + "integrity": "sha512-gW2vfkytNGgMVADiuzdvOfw0mWG9za20F/1fCJsif5aBMAvWJTSbpIXbIe0XkOe0VENk+PadpQ7cZgUy2sUJcA==", "cpu": [ "wasm32" ], @@ -451,18 +475,18 @@ "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "1.10.0", - "@emnapi/runtime": "1.10.0", - "@napi-rs/wasm-runtime": "^1.1.4" + "@emnapi/core": "1.11.1", + "@emnapi/runtime": "1.11.1", + "@napi-rs/wasm-runtime": "^1.1.5" }, "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@oxc-parser/binding-win32-arm64-msvc": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.133.0.tgz", - "integrity": "sha512-KPTNDKbxH1cglrqTyVeXHb4Pk4oksz8EcE1/v8zqU7N4UXbiHfA/IwtXZ2U77fnRAWBbgVkl/lZbL7o3hRdejg==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.137.0.tgz", + "integrity": "sha512-x+pFANF0yL5uK/6T7lu6SlR5qid6sp//eZXKLq5iNsIE+EQg6EaS8/wsW7E91nXXjpnPhSoMOHXShSVhGRdn8w==", "cpu": [ "arm64" ], @@ -477,9 +501,9 @@ } }, "node_modules/@oxc-parser/binding-win32-ia32-msvc": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.133.0.tgz", - "integrity": "sha512-Una1bNYv9zCavQrfnDR9wuZVB3itLjCEH4Oz7i6CwAJN/Xq9b+zbbcxmvdkKvvJt4Ngc/MBmIYlbLo3zS4TQ0A==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.137.0.tgz", + "integrity": "sha512-sQUqym80PFi6McRsIqfJrSu2JrSClEZIXXD+/FjAFoULEKzOPsldIdFBG96xdX8aVMzCNQ9792FPx3MfkEIrFA==", "cpu": [ "ia32" ], @@ -494,9 +518,9 @@ } }, "node_modules/@oxc-parser/binding-win32-x64-msvc": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.133.0.tgz", - "integrity": "sha512-kjBhCiOGSYTwDJQuuZa7a94JbP8htWu7J0X1KwH74kV2K5eYf6eyJRYmkpCDvr0XEL8tMxYI4WU1VekblFCLgg==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.137.0.tgz", + "integrity": "sha512-2AsevxlvNN4WKxpEn3RtqD5zbqMaXF+T7JXblsP4gVuY+vC9dXS4ED/PwfRCliFqoeisYS3Iro4DHzxr0TEvVA==", "cpu": [ "x64" ], @@ -511,9 +535,9 @@ } }, "node_modules/@oxc-project/types": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz", - "integrity": "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.137.0.tgz", + "integrity": "sha512-WT+Gb24i8hmvo85AIv2oEYouEXkRlKAlT9WaCa3TfLgNCN+GhrJOGZuIlMouAh38Qe4QOx26eUOVsq70qXrywA==", "dev": true, "license": "MIT", "funding": { @@ -521,9 +545,9 @@ } }, "node_modules/@oxc-resolver/binding-android-arm-eabi": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm-eabi/-/binding-android-arm-eabi-11.20.0.tgz", - "integrity": "sha512-IjfWOXRgJFNdORDl+Uf1aibNgZY2guOD3zmOhx1BGVb/MIiqlFTdmjpQNplSN58lhWehnX4UNqC3QwpUo8pjJg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm-eabi/-/binding-android-arm-eabi-11.21.3.tgz", + "integrity": "sha512-eNU11A2WNizh04v3uyaJCootrHIaS0B9aHYXvAvVnPNk4xYSjMUjHnhQ6dewPN2MRYDskV85d1N0Aw0WNWhcyg==", "cpu": [ "arm" ], @@ -535,9 +559,9 @@ ] }, "node_modules/@oxc-resolver/binding-android-arm64": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm64/-/binding-android-arm64-11.20.0.tgz", - "integrity": "sha512-QqslZAuFQG8Q9xm7JuIn8JUbvywhSBMVhuQHtYW+auirZJloS41oxUUaBXk7uUhZJgp44c5zQLeVvmFaDQB+2Q==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm64/-/binding-android-arm64-11.21.3.tgz", + "integrity": "sha512-8Q+ZjTLvn2dIcWsrmhdrEihm7q+ag/k+mkry7Z+t0QbbHaVxXQfvH9AewyVMh/WrpEKhQ3DDgx9fYbqeCpeOEw==", "cpu": [ "arm64" ], @@ -549,9 +573,9 @@ ] }, "node_modules/@oxc-resolver/binding-darwin-arm64": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-arm64/-/binding-darwin-arm64-11.20.0.tgz", - "integrity": "sha512-MUcavykj2ewlR+kc5arpg4tC2RvzJkUxWtNv74pf7lcNk00GpIpN43vXMj+j6r4eMmfZhlb8hueKoIb8e9kAGQ==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-arm64/-/binding-darwin-arm64-11.21.3.tgz", + "integrity": "sha512-wkh0qKZGHXVUDxFw3oA1TXnU2BDYY/r775oJflGeIr8uDPPoN2pk8gijQIzYRT6hoql/lg3+Tx/SaTn9e2/aGg==", "cpu": [ "arm64" ], @@ -563,9 +587,9 @@ ] }, "node_modules/@oxc-resolver/binding-darwin-x64": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-x64/-/binding-darwin-x64-11.20.0.tgz", - "integrity": "sha512-BGB16nRUK5Etiv//ihPyzj8Lj1px0mhh4YIfe0FDf045ywknfSm0GEbiRESpr6Q4K82AvnyaRIhhluHByvS4bg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-x64/-/binding-darwin-x64-11.21.3.tgz", + "integrity": "sha512-HbNc23FAQYbuyDV2vBWMez4u4mrsm5RAkniGZAWqr6lYZ3N4beeqIb776jzwRl8qL2zRhHVXpUj97X0QgogVzg==", "cpu": [ "x64" ], @@ -577,9 +601,9 @@ ] }, "node_modules/@oxc-resolver/binding-freebsd-x64": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-freebsd-x64/-/binding-freebsd-x64-11.20.0.tgz", - "integrity": "sha512-JZgtePaqj3qmD5XFHJaSLWzHRxQu0LaPkdoM1KJXYADvAaa83ijXHclV3ej3CueeW0wxfIAbGCZVP45J0CA7uQ==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-freebsd-x64/-/binding-freebsd-x64-11.21.3.tgz", + "integrity": "sha512-K6xNsTUPEUdfrn0+kbMq5nOUB5w1C5pavPQngt4TM2FpN91lP0PBe2srSpamb4d69O7h86oAi/qWX/kZNRSjkw==", "cpu": [ "x64" ], @@ -591,9 +615,9 @@ ] }, "node_modules/@oxc-resolver/binding-linux-arm-gnueabihf": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-11.20.0.tgz", - "integrity": "sha512-hOQ/p3ry3v3SchUBXicrrnszaI/UmYzM4wtS4RGfwgVUX7a+HbyQSzJ5aOzu+o6XZkFkS3ZXN4PZAzhOb77OSg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-11.21.3.tgz", + "integrity": "sha512-VcFmOpcpWX1zoEy8M58tR2M9YxM+Z9RuQhqAx5q0CTmrruaP7Gveejg75hzd/5sg5nk9G3aLALEa3hE2FsmmTQ==", "cpu": [ "arm" ], @@ -605,9 +629,9 @@ ] }, "node_modules/@oxc-resolver/binding-linux-arm-musleabihf": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-11.20.0.tgz", - "integrity": "sha512-2ArPksaw0AqeuGBfoS715VF+JvJQAhD2niWgjE5hVO+L+nAfikVQopvngCMX9x4BD8itWoQ3dnikrQyl5Ho5Jg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-11.21.3.tgz", + "integrity": "sha512-quVoxFLBy43hWaQbbDtQNRwAX5vX76mv7n64icAtQcJ3eNgVeblqmkupF/hAneNthdqSlnd1sTjb3aQSaDPaCQ==", "cpu": [ "arm" ], @@ -619,13 +643,16 @@ ] }, "node_modules/@oxc-resolver/binding-linux-arm64-gnu": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-11.20.0.tgz", - "integrity": "sha512-0bJnmYFp62JdZ4nVMDUZ/C58BCZOCcqgKtnUlp7L9Ojf/czIN+3j72YlLPeWLkzlr6SlYvIQA4SGV/HyO0d+qg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-11.21.3.tgz", + "integrity": "sha512-X0AqNZgcD07Q4V3RDK18/vYOj/HQT/FnmEFGYS2jTWqY7JO13ryE3TEs3eAIgUJhBnNkpEaiXqz3VK8M7qQhWQ==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -633,13 +660,16 @@ ] }, "node_modules/@oxc-resolver/binding-linux-arm64-musl": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-musl/-/binding-linux-arm64-musl-11.20.0.tgz", - "integrity": "sha512-wKHHzPKZo7Ufhv/Bt6yxT7FOgnIgW4gwXcJUipkShGp68W3wGVqvr1Sr0fY65lN0Oy6y41+g2kIDvkgZaMMUkw==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-musl/-/binding-linux-arm64-musl-11.21.3.tgz", + "integrity": "sha512-YkaQnaKYdbuaXvRt5Qd0GpbihzVnyfR6z1SpYfIUC6RTu4NF7lDKPjVkYb+jRI2gedVO2rVpN35Y6akG6ud4Lw==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -647,13 +677,16 @@ ] }, "node_modules/@oxc-resolver/binding-linux-ppc64-gnu": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-11.20.0.tgz", - "integrity": "sha512-RN8goF7Ie0B79L4i4G6OeBocTgSC56vJbQ65VJje+oXnldVpLnOU7j/AQ/dP94TcCS+Yh6WG8u3Qt4ETteXFNQ==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-11.21.3.tgz", + "integrity": "sha512-gB9HwhrPiFqUzDeEq+y/CgAijz1YdI6BnXz5GaH2Pa9cWdutchlkGFAiAuGb/PjVQpiK6NFKzFuztxrweoit7A==", "cpu": [ "ppc64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -661,13 +694,16 @@ ] }, "node_modules/@oxc-resolver/binding-linux-riscv64-gnu": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-11.20.0.tgz", - "integrity": "sha512-5l1yU6/xQEqLZRzxqmMxJfWPslpwCmBsdDGaBvABPehxquCXDC7dd7oraNdKSJUMDXSM7VvVj8H2D2FTjU7oWw==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-11.21.3.tgz", + "integrity": "sha512-zjDWBlYk8QGv0H8dsPUWqkfjYIIjG2TvspGkzXL0eImbgxtZorA/klKeHyolevoT3Kvbi+1iMr9Lhrh7jf54Og==", "cpu": [ "riscv64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -675,13 +711,16 @@ ] }, "node_modules/@oxc-resolver/binding-linux-riscv64-musl": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-11.20.0.tgz", - "integrity": "sha512-xHEvkbgz6UC+A3JOyDQy76LkUaxsNSfIr3/GV8slwZsnuooJiIB34gzJfsyvR4JdCYNUUPsRJc/w/oWkODu+hg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-11.21.3.tgz", + "integrity": "sha512-4UfsQvacV388y1zpXL7C1x1FNYaV52JtuNRiuzrfQA2z1z6ElVrsidkGsrvQ5EgeSq1Pj7kaKqrgGkvFuxJ/tw==", "cpu": [ "riscv64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -689,13 +728,16 @@ ] }, "node_modules/@oxc-resolver/binding-linux-s390x-gnu": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-11.20.0.tgz", - "integrity": "sha512-aWPDUUmSeyHvlW+SoEUd+JIJsQhVhu6a5tBpDRMu058naPAchTgAVGCFy35zjbnFlt0i8hLWziff6HX0D3LU4g==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-11.21.3.tgz", + "integrity": "sha512-b5uH+HKH0MP5mNBYaK75SKsJbw52URqrx2LavYdq6wb0l3ExAG5niYRP9DWUNHdKilpaBVM2bXk9HNWrH3ew7Q==", "cpu": [ "s390x" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -703,13 +745,16 @@ ] }, "node_modules/@oxc-resolver/binding-linux-x64-gnu": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-gnu/-/binding-linux-x64-gnu-11.20.0.tgz", - "integrity": "sha512-x2YeSimvhJjKLVD8KSu8f/rqU1potcdEMkApIPJqjZWN7c2Fpt4g2X32WDg1p+XDAmyT7nuQGe0vnhvXeLbH+g==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-gnu/-/binding-linux-x64-gnu-11.21.3.tgz", + "integrity": "sha512-PjYlmilBpNRh2ntXNYAK3Am5w/nPfEpnU/96iNx7CI8EzAn12J4JRiec63wHJTH31nLoCNxBg/829pN+3CfG3Q==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -717,13 +762,16 @@ ] }, "node_modules/@oxc-resolver/binding-linux-x64-musl": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-musl/-/binding-linux-x64-musl-11.20.0.tgz", - "integrity": "sha512-kcRLEIxpZefeYfLChjpgFf3ilBzRDZ+yobMrpRsQlSrxuFGtm3U6PMU7AaEpMqo3NfDGVyJJseAjnRLzMFHjwQ==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-musl/-/binding-linux-x64-musl-11.21.3.tgz", + "integrity": "sha512-QTBAb7JuHlZ7JUEyM8UiQi2f7m/L4swBhP2TNpYIDc9Wp/wRw1G/8sl6i13aIzQAXH7LKIm294LeOHd0lQR8zA==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -731,9 +779,9 @@ ] }, "node_modules/@oxc-resolver/binding-openharmony-arm64": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-openharmony-arm64/-/binding-openharmony-arm64-11.20.0.tgz", - "integrity": "sha512-HHcfnApSZGtKhTiHqe8OZruOZe5XuFQH5/E0Yhj3u8fnFvzkM4/k6WjacUf4SvA0SPEAbfbgYmVPuo0VX/fIBQ==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-openharmony-arm64/-/binding-openharmony-arm64-11.21.3.tgz", + "integrity": "sha512-4j1DFwjwv36ec9kds0jU/ucQ5Ha4ERO/H95BxR5JFf0kqUUAJ1kwII7XhTc1vZrkdJkvLGC9Q2MbpObpum8RBg==", "cpu": [ "arm64" ], @@ -745,9 +793,9 @@ ] }, "node_modules/@oxc-resolver/binding-wasm32-wasi": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-wasm32-wasi/-/binding-wasm32-wasi-11.20.0.tgz", - "integrity": "sha512-Tn0y1XOFYHNfK1wp1Z5QK8Rcld/bsOwRISQXfqAZ5IBpv8Gz1IvV39fUWNprqNdRizgcvFhOzWwFun2zkJsyBg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-wasm32-wasi/-/binding-wasm32-wasi-11.21.3.tgz", + "integrity": "sha512-i8oluoel5kru/j1WNrjmQSiA3GQ7wvIYVR1IwIoZtKogAhya2iub+ZKIeSIkcJOrnzQ18Tzl/F+kL3fYOxZLvA==", "cpu": [ "wasm32" ], @@ -755,18 +803,41 @@ "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "1.10.0", - "@emnapi/runtime": "1.10.0", - "@napi-rs/wasm-runtime": "^1.1.4" + "@emnapi/core": "1.11.0", + "@emnapi/runtime": "1.11.0", + "@napi-rs/wasm-runtime": "^1.1.5" }, "engines": { "node": ">=14.0.0" } }, + "node_modules/@oxc-resolver/binding-wasm32-wasi/node_modules/@emnapi/core": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.0.tgz", + "integrity": "sha512-l9Oo58x0HOP5znGzVhYW9U3e5wVuA4LAZU2AGezTmkhO1CgQRFDhDg4nneHsu/t3WniXg9QrG2nIXL/ZS8ln8Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@oxc-resolver/binding-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.0.tgz", + "integrity": "sha512-55coeOFKHv1ywEcUXJtWU5f+Jr/W5tZDvZig8DLKSwUN1JpROQ4rk/SNOQiFWmaR/VKF4zuFyW1B8JduOSv6Pg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@oxc-resolver/binding-win32-arm64-msvc": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-11.20.0.tgz", - "integrity": "sha512-qPi25YNPe4YenS8MgsQU2+bIFHxxpLx1LVna2444cEHqNPhNjvWf9zqj4aWE43H9LpAsTmkkAlA3eL5ElBU3mA==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-11.21.3.tgz", + "integrity": "sha512-M/8dw8dD6aOs+NlPJax401CZB9I7Aut84isQLgALGGwke4Afvw+/7yYhZb94yXf6t2sPLhQLmSmtSV+2FhsOWg==", "cpu": [ "arm64" ], @@ -778,9 +849,9 @@ ] }, "node_modules/@oxc-resolver/binding-win32-x64-msvc": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-x64-msvc/-/binding-win32-x64-msvc-11.20.0.tgz", - "integrity": "sha512-Wb14jWEW8huH6It9F6sXd9vrYmIS7pMrgkU6sxpLxkP+9z+wRgs71hUEhRpcn8FOXAFa27FVWfY2tRpbfTzfLw==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-x64-msvc/-/binding-win32-x64-msvc-11.21.3.tgz", + "integrity": "sha512-H7BCt/VnS9hnmMp42eGhZ99izSCRvlnWwy/N71K1/J8QoExwY4262Z8QiEkMDtduRJrztayDxETTckmUuAVL9Q==", "cpu": [ "x64" ], @@ -905,9 +976,9 @@ "license": "MIT" }, "node_modules/@tybys/wasm-util": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", - "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", "dev": true, "license": "MIT", "optional": true, @@ -1519,9 +1590,9 @@ } }, "node_modules/knip": { - "version": "6.16.1", - "resolved": "https://registry.npmjs.org/knip/-/knip-6.16.1.tgz", - "integrity": "sha512-TKMn1rxgH6h9vXR9Y0B+Cq7AdPTr9EI02IwoT65NzqYUkvoDQAaJ/aPybiFpAhZ1px6cNYYwXf86iHkBgzCo9w==", + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/knip/-/knip-6.26.0.tgz", + "integrity": "sha512-e9eELEEpBpGTd4H4HB7818/DYj9dMzMyUqAddfYwUN/EbSkgIjOuWEF96W/xHsmV0SDrsdXjIM+oZ2xpPzPsBA==", "dev": true, "funding": [ { @@ -1539,13 +1610,13 @@ "formatly": "^0.3.0", "get-tsconfig": "4.14.0", "jiti": "^2.7.0", - "oxc-parser": "^0.133.0", - "oxc-resolver": "^11.20.0", + "oxc-parser": "^0.137.0", + "oxc-resolver": "11.21.3", "picomatch": "^4.0.4", "smol-toml": "^1.6.1", "strip-json-comments": "5.0.3", - "tinyglobby": "^0.2.16", - "unbash": "^3.0.0", + "tinyglobby": "^0.2.17", + "unbash": "^4.0.1", "yaml": "^2.9.0", "zod": "^4.1.11" }, @@ -1677,13 +1748,13 @@ "license": "MIT" }, "node_modules/oxc-parser": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/oxc-parser/-/oxc-parser-0.133.0.tgz", - "integrity": "sha512-661RSx+ZcjBmjBYid+Fpp/2F5EbtildpeoZh5HdgnGs+jZ03nqQEQW8yGkt4BGyOC3OMPDQQRl8M5kqD2/g6jw==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/oxc-parser/-/oxc-parser-0.137.0.tgz", + "integrity": "sha512-yFImD+WLElJpLKy8llG1qe4DCmMsL18peRp8XP1JKfig/gISbJkglnpDtX2aTmAn10kZF7164HbN2H8QPsXxGg==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "^0.133.0" + "@oxc-project/types": "^0.137.0" }, "engines": { "node": "^20.19.0 || >=22.12.0" @@ -1692,57 +1763,57 @@ "url": "https://github.com/sponsors/Boshen" }, "optionalDependencies": { - "@oxc-parser/binding-android-arm-eabi": "0.133.0", - "@oxc-parser/binding-android-arm64": "0.133.0", - "@oxc-parser/binding-darwin-arm64": "0.133.0", - "@oxc-parser/binding-darwin-x64": "0.133.0", - "@oxc-parser/binding-freebsd-x64": "0.133.0", - "@oxc-parser/binding-linux-arm-gnueabihf": "0.133.0", - "@oxc-parser/binding-linux-arm-musleabihf": "0.133.0", - "@oxc-parser/binding-linux-arm64-gnu": "0.133.0", - "@oxc-parser/binding-linux-arm64-musl": "0.133.0", - "@oxc-parser/binding-linux-ppc64-gnu": "0.133.0", - "@oxc-parser/binding-linux-riscv64-gnu": "0.133.0", - "@oxc-parser/binding-linux-riscv64-musl": "0.133.0", - "@oxc-parser/binding-linux-s390x-gnu": "0.133.0", - "@oxc-parser/binding-linux-x64-gnu": "0.133.0", - "@oxc-parser/binding-linux-x64-musl": "0.133.0", - "@oxc-parser/binding-openharmony-arm64": "0.133.0", - "@oxc-parser/binding-wasm32-wasi": "0.133.0", - "@oxc-parser/binding-win32-arm64-msvc": "0.133.0", - "@oxc-parser/binding-win32-ia32-msvc": "0.133.0", - "@oxc-parser/binding-win32-x64-msvc": "0.133.0" + "@oxc-parser/binding-android-arm-eabi": "0.137.0", + "@oxc-parser/binding-android-arm64": "0.137.0", + "@oxc-parser/binding-darwin-arm64": "0.137.0", + "@oxc-parser/binding-darwin-x64": "0.137.0", + "@oxc-parser/binding-freebsd-x64": "0.137.0", + "@oxc-parser/binding-linux-arm-gnueabihf": "0.137.0", + "@oxc-parser/binding-linux-arm-musleabihf": "0.137.0", + "@oxc-parser/binding-linux-arm64-gnu": "0.137.0", + "@oxc-parser/binding-linux-arm64-musl": "0.137.0", + "@oxc-parser/binding-linux-ppc64-gnu": "0.137.0", + "@oxc-parser/binding-linux-riscv64-gnu": "0.137.0", + "@oxc-parser/binding-linux-riscv64-musl": "0.137.0", + "@oxc-parser/binding-linux-s390x-gnu": "0.137.0", + "@oxc-parser/binding-linux-x64-gnu": "0.137.0", + "@oxc-parser/binding-linux-x64-musl": "0.137.0", + "@oxc-parser/binding-openharmony-arm64": "0.137.0", + "@oxc-parser/binding-wasm32-wasi": "0.137.0", + "@oxc-parser/binding-win32-arm64-msvc": "0.137.0", + "@oxc-parser/binding-win32-ia32-msvc": "0.137.0", + "@oxc-parser/binding-win32-x64-msvc": "0.137.0" } }, "node_modules/oxc-resolver": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/oxc-resolver/-/oxc-resolver-11.20.0.tgz", - "integrity": "sha512-CblytBiV/a/ZXY34dsVU2NxhIOxMXst8CvDCtyBelVITgd7PLrKzbEbA6oKLdPjvDKDzCiW48qzmzZ+mYaqn+g==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/oxc-resolver/-/oxc-resolver-11.21.3.tgz", + "integrity": "sha512-2Mx3fKQz7+xgrBONjsxOgCGtMHOn38/HxMzW1I5efwXB5a4lRN0Vp40gYUJFBWJslcrvwoofTrqoTnLbwTd3pA==", "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/Boshen" }, "optionalDependencies": { - "@oxc-resolver/binding-android-arm-eabi": "11.20.0", - "@oxc-resolver/binding-android-arm64": "11.20.0", - "@oxc-resolver/binding-darwin-arm64": "11.20.0", - "@oxc-resolver/binding-darwin-x64": "11.20.0", - "@oxc-resolver/binding-freebsd-x64": "11.20.0", - "@oxc-resolver/binding-linux-arm-gnueabihf": "11.20.0", - "@oxc-resolver/binding-linux-arm-musleabihf": "11.20.0", - "@oxc-resolver/binding-linux-arm64-gnu": "11.20.0", - "@oxc-resolver/binding-linux-arm64-musl": "11.20.0", - "@oxc-resolver/binding-linux-ppc64-gnu": "11.20.0", - "@oxc-resolver/binding-linux-riscv64-gnu": "11.20.0", - "@oxc-resolver/binding-linux-riscv64-musl": "11.20.0", - "@oxc-resolver/binding-linux-s390x-gnu": "11.20.0", - "@oxc-resolver/binding-linux-x64-gnu": "11.20.0", - "@oxc-resolver/binding-linux-x64-musl": "11.20.0", - "@oxc-resolver/binding-openharmony-arm64": "11.20.0", - "@oxc-resolver/binding-wasm32-wasi": "11.20.0", - "@oxc-resolver/binding-win32-arm64-msvc": "11.20.0", - "@oxc-resolver/binding-win32-x64-msvc": "11.20.0" + "@oxc-resolver/binding-android-arm-eabi": "11.21.3", + "@oxc-resolver/binding-android-arm64": "11.21.3", + "@oxc-resolver/binding-darwin-arm64": "11.21.3", + "@oxc-resolver/binding-darwin-x64": "11.21.3", + "@oxc-resolver/binding-freebsd-x64": "11.21.3", + "@oxc-resolver/binding-linux-arm-gnueabihf": "11.21.3", + "@oxc-resolver/binding-linux-arm-musleabihf": "11.21.3", + "@oxc-resolver/binding-linux-arm64-gnu": "11.21.3", + "@oxc-resolver/binding-linux-arm64-musl": "11.21.3", + "@oxc-resolver/binding-linux-ppc64-gnu": "11.21.3", + "@oxc-resolver/binding-linux-riscv64-gnu": "11.21.3", + "@oxc-resolver/binding-linux-riscv64-musl": "11.21.3", + "@oxc-resolver/binding-linux-s390x-gnu": "11.21.3", + "@oxc-resolver/binding-linux-x64-gnu": "11.21.3", + "@oxc-resolver/binding-linux-x64-musl": "11.21.3", + "@oxc-resolver/binding-openharmony-arm64": "11.21.3", + "@oxc-resolver/binding-wasm32-wasi": "11.21.3", + "@oxc-resolver/binding-win32-arm64-msvc": "11.21.3", + "@oxc-resolver/binding-win32-x64-msvc": "11.21.3" } }, "node_modules/path-parse": { @@ -2350,9 +2421,9 @@ } }, "node_modules/unbash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unbash/-/unbash-3.0.0.tgz", - "integrity": "sha512-FeFPZ/WFT0mbRCuydiZzpPFlrYN8ZUpphQKoq4EeElVIYjYyGzPMxQR/simUwCOJIyVhpFk4RbtyO7RuMpMnHA==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/unbash/-/unbash-4.0.3.tgz", + "integrity": "sha512-3cudTErfToSc4Ggv8XGXVNVli/xHKUtUZvaY5UVwhOcUPbQGz7PeaEnT/SAVgNziZtX67KEN9swMUYkLghxA1w==", "dev": true, "license": "ISC", "engines": { diff --git a/db/package.json b/db/package.json index c8c1a864..c5dec25b 100644 --- a/db/package.json +++ b/db/package.json @@ -35,7 +35,7 @@ "devDependencies": { "@types/pg": "^8.10.2", "dotenv": "^17.4.2", - "knip": "^6.16.1", + "knip": "^6.26.0", "typescript": "^6.0.3" } } From 343acbf0b6b4c4d5f807ed71103298d43c83b03a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Jul 2026 15:19:54 +0100 Subject: [PATCH 26/57] build(deps): bump markdown-it (#828) Bumps the client-prod-security group with 1 update in the /client directory: [markdown-it](https://github.com/markdown-it/markdown-it). Updates `markdown-it` from 14.1.1 to 14.3.0 - [Changelog](https://github.com/markdown-it/markdown-it/blob/master/CHANGELOG.md) - [Commits](https://github.com/markdown-it/markdown-it/compare/14.1.1...14.3.0) --- updated-dependencies: - dependency-name: markdown-it dependency-version: 14.2.0 dependency-type: indirect dependency-group: client-prod-security ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- client/package-lock.json | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index b5572bed..03674dbb 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -9808,9 +9808,19 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, "node_modules/linkify-it": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", - "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.2.tgz", + "integrity": "sha512-ONTm2jCMAVZjgQa/Fy1kScXsuOoF5NPTsoFBdE1KVIZ2vAh/r9+Bqo+0jINCBYnavTPQZz38QzFTme79ENoN3Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/markdown-it" + } + ], "license": "MIT", "peer": true, "dependencies": { @@ -9928,15 +9938,25 @@ } }, "node_modules/markdown-it": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.1.tgz", - "integrity": "sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==", + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.3.0.tgz", + "integrity": "sha512-RCEsPjR+sr0x+AuYp601tKTkgFG4YEPLCzHST3cQ/fhlJkqAkz1L2/Qbp1j9qw5SBwQHFBoW8+hoN5xssOF0Tw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/markdown-it" + } + ], "license": "MIT", "peer": true, "dependencies": { "argparse": "^2.0.1", - "entities": "^4.4.0", - "linkify-it": "^5.0.0", + "entities": "^4.5.0", + "linkify-it": "^5.0.2", "mdurl": "^2.0.0", "punycode.js": "^2.3.1", "uc.micro": "^2.1.0" From d93125d7d5dd40107e10e0f7daa3001a99397dbb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Jul 2026 15:20:56 +0100 Subject: [PATCH 27/57] build(deps): bump the migrator-prod group across 1 directory with 2 updates (#422) Bumps the migrator-prod group with 2 updates in the /migrator directory: [cassandra-driver](https://github.com/apache/cassandra-nodejs-driver) and [umzug](https://github.com/sequelize/umzug). Updates `cassandra-driver` from 4.8.0 to 4.9.0 - [Changelog](https://github.com/apache/cassandra-nodejs-driver/blob/trunk/CHANGELOG.md) - [Commits](https://github.com/apache/cassandra-nodejs-driver/compare/v4.8.0...v4.9.0) Updates `umzug` from 3.8.2 to 3.8.3 - [Release notes](https://github.com/sequelize/umzug/releases) - [Changelog](https://github.com/sequelize/umzug/blob/main/CHANGELOG.md) - [Commits](https://github.com/sequelize/umzug/compare/v3.8.2...v3.8.3) --- updated-dependencies: - dependency-name: cassandra-driver dependency-version: 4.9.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: migrator-prod - dependency-name: umzug dependency-version: 3.8.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: migrator-prod ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- migrator/package-lock.json | 806 ++++++++----------------------------- 1 file changed, 175 insertions(+), 631 deletions(-) diff --git a/migrator/package-lock.json b/migrator/package-lock.json index 830fb331..f16d0d77 100644 --- a/migrator/package-lock.json +++ b/migrator/package-lock.json @@ -75,41 +75,6 @@ "@emnapi/runtime": "^1.7.1" } }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/@oxc-parser/binding-android-arm-eabi": { "version": "0.133.0", "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm-eabi/-/binding-android-arm-eabi-0.133.0.tgz", @@ -734,19 +699,16 @@ ] }, "node_modules/@rushstack/node-core-library": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-5.13.0.tgz", - "integrity": "sha512-IGVhy+JgUacAdCGXKUrRhwHMTzqhWwZUI+qEPcdzsb80heOw0QPbhhoVsoiMF7Klp8eYsp7hzpScMXmOa3Uhfg==", - "license": "MIT", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-4.0.2.tgz", + "integrity": "sha512-hyES82QVpkfQMeBMteQUnrhASL/KHPhd7iJ8euduwNJG4mu2GSOKybf0rOEjOm1Wz7CwJEUm9y0yD7jg2C1bfg==", "dependencies": { - "ajv": "~8.13.0", - "ajv-draft-04": "~1.0.0", - "ajv-formats": "~3.0.1", - "fs-extra": "~11.3.0", + "fs-extra": "~7.0.1", "import-lazy": "~4.0.0", "jju": "~1.4.0", "resolve": "~1.22.1", - "semver": "~7.5.4" + "semver": "~7.5.4", + "z-schema": "~5.0.2" }, "peerDependencies": { "@types/node": "*" @@ -758,12 +720,11 @@ } }, "node_modules/@rushstack/terminal": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/@rushstack/terminal/-/terminal-0.15.2.tgz", - "integrity": "sha512-7Hmc0ysK5077R/IkLS9hYu0QuNafm+TbZbtYVzCMbeOdMjaRboLKrhryjwZSRJGJzu+TV1ON7qZHeqf58XfLpA==", - "license": "MIT", + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@rushstack/terminal/-/terminal-0.10.0.tgz", + "integrity": "sha512-UbELbXnUdc7EKwfH2sb8ChqNgapUOdqcCIdQP4NGxBpTZV2sQyeekuK3zmfQSa/MN+/7b4kBogl2wq0vpkpYGw==", "dependencies": { - "@rushstack/node-core-library": "5.13.0", + "@rushstack/node-core-library": "4.0.2", "supports-color": "~8.1.1" }, "peerDependencies": { @@ -776,12 +737,11 @@ } }, "node_modules/@rushstack/ts-command-line": { - "version": "4.23.7", - "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.23.7.tgz", - "integrity": "sha512-Gr9cB7DGe6uz5vq2wdr89WbVDKz0UeuFEn5H2CfWDe7JvjFFaiV15gi6mqDBTbHhHCWS7w8mF1h3BnIfUndqdA==", - "license": "MIT", + "version": "4.19.1", + "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.19.1.tgz", + "integrity": "sha512-J7H768dgcpG60d7skZ5uSSwyCZs/S2HrWP1Ds8d1qYAyaaeJmpmmLr9BVw97RjFzmQPOYnoXcKA4GkqDCkduQg==", "dependencies": { - "@rushstack/terminal": "0.15.2", + "@rushstack/terminal": "0.10.0", "@types/argparse": "1.0.38", "argparse": "~1.0.9", "string-argv": "~0.3.1" @@ -806,8 +766,7 @@ "node_modules/@types/argparse": { "version": "1.0.38", "resolved": "https://registry.npmjs.org/@types/argparse/-/argparse-1.0.38.tgz", - "integrity": "sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==", - "license": "MIT" + "integrity": "sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==" }, "node_modules/@types/debug": { "version": "4.1.8", @@ -859,53 +818,6 @@ "node": ">=6.0" } }, - "node_modules/ajv": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", - "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-draft-04": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", - "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", - "license": "MIT", - "peerDependencies": { - "ajv": "^8.5.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/ajv-formats": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", - "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", - "license": "MIT", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, "node_modules/ansi-regex": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", @@ -932,51 +844,35 @@ "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "license": "MIT", "dependencies": { "sprintf-js": "~1.0.2" } }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/cassandra-driver": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/cassandra-driver/-/cassandra-driver-4.8.0.tgz", - "integrity": "sha512-HritfMGq9V7SuESeSodHvArs0mLuMk7uh+7hQK2lqdvXrvm50aWxb4RPxkK3mPDdsgHjJ427xNRFITMH2ei+Sw==", - "license": "Apache-2.0", + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/cassandra-driver/-/cassandra-driver-4.9.0.tgz", + "integrity": "sha512-svYpdkLIGjD0WmuuwkkeYbfBdPX1zksK2cDyT1mWjX53OVTzuWBOVy54K6PPij8GgYpIG+K82OryrBv/xNeuWg==", "dependencies": { - "@types/node": "^18.11.18", + "@types/node": "^20.14.8", "adm-zip": "~0.5.10", "long": "~5.2.3" }, "engines": { - "node": ">=18" + "node": ">=20" } }, "node_modules/cassandra-driver/node_modules/@types/node": { - "version": "18.19.130", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.130.tgz", - "integrity": "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==", - "license": "MIT", + "version": "20.19.43", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz", + "integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~6.21.0" } }, "node_modules/cassandra-driver/node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "license": "MIT" + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==" }, "node_modules/cliui": { "version": "9.0.1", @@ -991,6 +887,15 @@ "node": ">=20" } }, + "node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "optional": true, + "engines": { + "node": "^12.20.0 || >=14" + } + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -1034,7 +939,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", "engines": { "node": ">= 0.4" } @@ -1047,52 +951,6 @@ "node": ">=6" } }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "license": "MIT" - }, - "node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-uri": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.4.tgz", - "integrity": "sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ] - }, - "node_modules/fastq": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", - "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, "node_modules/fd-package-json": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fd-package-json/-/fd-package-json-2.0.0.tgz", @@ -1103,18 +961,6 @@ "walk-up-path": "^4.0.0" } }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/formatly": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/formatly/-/formatly-0.3.0.tgz", @@ -1132,24 +978,22 @@ } }, "node_modules/fs-extra": { - "version": "11.3.4", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.4.tgz", - "integrity": "sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA==", - "license": "MIT", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" }, "engines": { - "node": ">=14.14" + "node": ">=6 <7 || >=8" } }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -1186,38 +1030,23 @@ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "license": "ISC" + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/hasown": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", - "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", - "license": "MIT", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", "dependencies": { "function-bind": "^1.1.2" }, @@ -1229,7 +1058,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", - "license": "MIT", "engines": { "node": ">=8" } @@ -1243,12 +1071,11 @@ ] }, "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "license": "MIT", + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", "dependencies": { - "hasown": "^2.0.2" + "hasown": "^2.0.3" }, "engines": { "node": ">= 0.4" @@ -1257,36 +1084,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, "node_modules/jiti": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", @@ -1300,23 +1097,12 @@ "node_modules/jju": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", - "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==", - "license": "MIT" - }, - "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "license": "MIT" + "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==" }, "node_modules/jsonfile": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", - "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", "optionalDependencies": { "graceful-fs": "^4.1.6" } @@ -1397,6 +1183,18 @@ "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", "license": "MIT" }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", + "deprecated": "This package is deprecated. Use the optional chaining (?.) operator instead." + }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", + "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead." + }, "node_modules/long": { "version": "5.2.5", "resolved": "https://registry.npmjs.org/long/-/long-5.2.5.tgz", @@ -1414,28 +1212,6 @@ "node": ">=10" } }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, "node_modules/moment": { "version": "2.29.4", "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", @@ -1532,26 +1308,13 @@ "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "license": "MIT" + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "node_modules/pg-connection-string": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz", "integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==" }, - "node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/pony-cause": { "version": "2.1.11", "resolved": "https://registry.npmjs.org/pony-cause/-/pony-cause-2.1.11.tgz", @@ -1561,40 +1324,10 @@ "node": ">=12.0.0" } }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/resolve": { "version": "1.22.12", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", - "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "is-core-module": "^2.16.1", @@ -1626,39 +1359,6 @@ "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-7.0.4.tgz", "integrity": "sha512-XgmCoxKWkDofwH8WddD0w85ZfqYz+ZHlr5yo+3YUCfycWawU56T5ckWXsScsj5B8tqUcIG67DxXByo3VUgiAdA==" }, - "node_modules/reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, "node_modules/semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", @@ -1759,14 +1459,12 @@ "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "license": "BSD-3-Clause" + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" }, "node_modules/string-argv": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", - "license": "MIT", "engines": { "node": ">=0.6.19" } @@ -1818,7 +1516,6 @@ "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -1833,7 +1530,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -1845,7 +1541,6 @@ "version": "0.2.17", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", - "dev": true, "license": "MIT", "dependencies": { "fdir": "^6.5.0", @@ -1862,7 +1557,6 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, "license": "MIT", "engines": { "node": ">=12.0.0" @@ -1880,7 +1574,6 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -1889,18 +1582,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, "node_modules/toposort-class": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toposort-class/-/toposort-class-1.0.1.tgz", @@ -1940,15 +1621,14 @@ } }, "node_modules/umzug": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/umzug/-/umzug-3.8.2.tgz", - "integrity": "sha512-BEWEF8OJjTYVC56GjELeHl/1XjFejrD7aHzn+HldRJTx+pL1siBrKHZC8n4K/xL3bEzVA9o++qD1tK2CpZu4KA==", - "license": "MIT", + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/umzug/-/umzug-3.8.3.tgz", + "integrity": "sha512-U9SRJI6LJvV0XwrqGMVPBkE26WHJklHZjtscJ2sEjUp7f+h4NH/25YGjPBernWLroVJvMnTkCAGC0bT0dd63qA==", "dependencies": { - "@rushstack/ts-command-line": "^4.12.2", + "@rushstack/ts-command-line": "4.19.1", "emittery": "^0.13.0", - "fast-glob": "^3.3.2", "pony-cause": "^2.1.4", + "tinyglobby": "^0.2.16", "type-fest": "^4.0.0" }, "engines": { @@ -1972,12 +1652,11 @@ "license": "MIT" }, "node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "license": "MIT", + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "engines": { - "node": ">= 10.0.0" + "node": ">= 4.0.0" } }, "node_modules/uuid": { @@ -2089,6 +1768,25 @@ "node": "^20.19.0 || ^22.12.0 || >=23" } }, + "node_modules/z-schema": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.5.tgz", + "integrity": "sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==", + "dependencies": { + "lodash.get": "^4.4.2", + "lodash.isequal": "^4.5.0", + "validator": "^13.7.0" + }, + "bin": { + "z-schema": "bin/z-schema" + }, + "engines": { + "node": ">=8.0.0" + }, + "optionalDependencies": { + "commander": "^9.4.1" + } + }, "node_modules/zod": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", @@ -2142,29 +1840,6 @@ "@tybys/wasm-util": "^0.10.1" } }, - "@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==" - }, - "@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - } - }, "@oxc-parser/binding-android-arm-eabi": { "version": "0.133.0", "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm-eabi/-/binding-android-arm-eabi-0.133.0.tgz", @@ -2455,35 +2130,33 @@ "optional": true }, "@rushstack/node-core-library": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-5.13.0.tgz", - "integrity": "sha512-IGVhy+JgUacAdCGXKUrRhwHMTzqhWwZUI+qEPcdzsb80heOw0QPbhhoVsoiMF7Klp8eYsp7hzpScMXmOa3Uhfg==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-4.0.2.tgz", + "integrity": "sha512-hyES82QVpkfQMeBMteQUnrhASL/KHPhd7iJ8euduwNJG4mu2GSOKybf0rOEjOm1Wz7CwJEUm9y0yD7jg2C1bfg==", "requires": { - "ajv": "~8.18.0", - "ajv-draft-04": "~1.0.0", - "ajv-formats": "~3.0.1", - "fs-extra": "~11.3.0", + "fs-extra": "~7.0.1", "import-lazy": "~4.0.0", "jju": "~1.4.0", "resolve": "~1.22.1", - "semver": "~7.5.4" + "semver": "~7.5.4", + "z-schema": "~5.0.2" } }, "@rushstack/terminal": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/@rushstack/terminal/-/terminal-0.15.2.tgz", - "integrity": "sha512-7Hmc0ysK5077R/IkLS9hYu0QuNafm+TbZbtYVzCMbeOdMjaRboLKrhryjwZSRJGJzu+TV1ON7qZHeqf58XfLpA==", + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@rushstack/terminal/-/terminal-0.10.0.tgz", + "integrity": "sha512-UbELbXnUdc7EKwfH2sb8ChqNgapUOdqcCIdQP4NGxBpTZV2sQyeekuK3zmfQSa/MN+/7b4kBogl2wq0vpkpYGw==", "requires": { - "@rushstack/node-core-library": "5.13.0", + "@rushstack/node-core-library": "4.0.2", "supports-color": "~8.1.1" } }, "@rushstack/ts-command-line": { - "version": "4.23.7", - "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.23.7.tgz", - "integrity": "sha512-Gr9cB7DGe6uz5vq2wdr89WbVDKz0UeuFEn5H2CfWDe7JvjFFaiV15gi6mqDBTbHhHCWS7w8mF1h3BnIfUndqdA==", + "version": "4.19.1", + "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.19.1.tgz", + "integrity": "sha512-J7H768dgcpG60d7skZ5uSSwyCZs/S2HrWP1Ds8d1qYAyaaeJmpmmLr9BVw97RjFzmQPOYnoXcKA4GkqDCkduQg==", "requires": { - "@rushstack/terminal": "0.15.2", + "@rushstack/terminal": "0.10.0", "@types/argparse": "1.0.38", "argparse": "~1.0.9", "string-argv": "~0.3.1" @@ -2555,31 +2228,6 @@ "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.10.tgz", "integrity": "sha512-x0HvcHqVJNTPk/Bw8JbLWlWoo6Wwnsug0fnYYro1HBrjxZ3G7/AZk7Ahv8JwDe1uIcz8eBqvu86FuF1POiG7vQ==" }, - "ajv": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", - "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", - "requires": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.1.2", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - } - }, - "ajv-draft-04": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", - "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", - "requires": {} - }, - "ajv-formats": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", - "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", - "requires": { - "ajv": "~8.18.0" - } - }, "ansi-regex": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", @@ -2598,36 +2246,28 @@ "sprintf-js": "~1.0.2" } }, - "braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "requires": { - "fill-range": "^7.1.1" - } - }, "cassandra-driver": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/cassandra-driver/-/cassandra-driver-4.8.0.tgz", - "integrity": "sha512-HritfMGq9V7SuESeSodHvArs0mLuMk7uh+7hQK2lqdvXrvm50aWxb4RPxkK3mPDdsgHjJ427xNRFITMH2ei+Sw==", + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/cassandra-driver/-/cassandra-driver-4.9.0.tgz", + "integrity": "sha512-svYpdkLIGjD0WmuuwkkeYbfBdPX1zksK2cDyT1mWjX53OVTzuWBOVy54K6PPij8GgYpIG+K82OryrBv/xNeuWg==", "requires": { - "@types/node": "^18.11.18", + "@types/node": "^20.14.8", "adm-zip": "~0.5.10", "long": "~5.2.3" }, "dependencies": { "@types/node": { - "version": "18.19.130", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.130.tgz", - "integrity": "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==", + "version": "20.19.43", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz", + "integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==", "requires": { - "undici-types": "~5.26.4" + "undici-types": "~6.21.0" } }, "undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==" } } }, @@ -2641,6 +2281,12 @@ "wrap-ansi": "^9.0.0" } }, + "commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "optional": true + }, "debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -2674,36 +2320,6 @@ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - } - }, - "fast-uri": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.4.tgz", - "integrity": "sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw==" - }, - "fastq": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", - "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", - "requires": { - "reusify": "^1.0.4" - } - }, "fd-package-json": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fd-package-json/-/fd-package-json-2.0.0.tgz", @@ -2713,14 +2329,6 @@ "walk-up-path": "^4.0.0" } }, - "fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "requires": { - "to-regex-range": "^5.0.1" - } - }, "formatly": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/formatly/-/formatly-0.3.0.tgz", @@ -2731,13 +2339,13 @@ } }, "fs-extra": { - "version": "11.3.4", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.4.tgz", - "integrity": "sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" } }, "function-bind": { @@ -2764,14 +2372,6 @@ "resolve-pkg-maps": "^1.0.0" } }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "requires": { - "is-glob": "^4.0.1" - } - }, "graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -2783,9 +2383,9 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, "hasown": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", - "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", "requires": { "function-bind": "^1.1.2" } @@ -2801,31 +2401,13 @@ "integrity": "sha512-6I/HUDeYFfuNCVS3td055BaXBwKYuzw7K3ExVMStBowKo9oOAMJIXIHvdyR3iboTCp1b+1i5DSkIZTcwIktuDw==" }, "is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "requires": { - "hasown": "^2.0.2" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", "requires": { - "is-extglob": "^2.1.1" + "hasown": "^2.0.3" } }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" - }, "jiti": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", @@ -2837,18 +2419,12 @@ "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==" }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, "jsonfile": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", - "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^2.0.0" + "graceful-fs": "^4.1.6" } }, "knip": { @@ -2892,6 +2468,16 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==" }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==" + }, + "lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" + }, "long": { "version": "5.2.5", "resolved": "https://registry.npmjs.org/long/-/long-5.2.5.tgz", @@ -2905,20 +2491,6 @@ "yallist": "^4.0.0" } }, - "merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" - }, - "micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "requires": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - } - }, "moment": { "version": "2.29.4", "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", @@ -3003,26 +2575,11 @@ "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz", "integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==" }, - "picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==" - }, "pony-cause": { "version": "2.1.11", "resolved": "https://registry.npmjs.org/pony-cause/-/pony-cause-2.1.11.tgz", "integrity": "sha512-M7LhCsdNbNgiLYiP4WjsfLUuFmCfnjdF6jKe2R9NKl4WFN+HZPGHJZ9lnLP7f9ZnKe3U9nuWD0szirmj+migUg==" }, - "queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" - }, - "require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" - }, "resolve": { "version": "1.22.12", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", @@ -3045,19 +2602,6 @@ "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-7.0.4.tgz", "integrity": "sha512-XgmCoxKWkDofwH8WddD0w85ZfqYz+ZHlr5yo+3YUCfycWawU56T5ckWXsScsj5B8tqUcIG67DxXByo3VUgiAdA==" }, - "reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==" - }, - "run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "requires": { - "queue-microtask": "^1.2.2" - } - }, "semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", @@ -3151,7 +2695,6 @@ "version": "0.2.17", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", - "dev": true, "requires": { "fdir": "^6.5.0", "picomatch": "^4.0.4" @@ -3161,25 +2704,15 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, "requires": {} }, "picomatch": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "dev": true + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==" } } }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "requires": { - "is-number": "^7.0.0" - } - }, "toposort-class": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toposort-class/-/toposort-class-1.0.1.tgz", @@ -3204,14 +2737,14 @@ "dev": true }, "umzug": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/umzug/-/umzug-3.8.2.tgz", - "integrity": "sha512-BEWEF8OJjTYVC56GjELeHl/1XjFejrD7aHzn+HldRJTx+pL1siBrKHZC8n4K/xL3bEzVA9o++qD1tK2CpZu4KA==", + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/umzug/-/umzug-3.8.3.tgz", + "integrity": "sha512-U9SRJI6LJvV0XwrqGMVPBkE26WHJklHZjtscJ2sEjUp7f+h4NH/25YGjPBernWLroVJvMnTkCAGC0bT0dd63qA==", "requires": { - "@rushstack/ts-command-line": "^4.12.2", + "@rushstack/ts-command-line": "4.19.1", "emittery": "^0.13.0", - "fast-glob": "^3.3.2", "pony-cause": "^2.1.4", + "tinyglobby": "^0.2.16", "type-fest": "^4.0.0" } }, @@ -3227,9 +2760,9 @@ "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==" }, "universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==" + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" }, "uuid": { "version": "14.0.0", @@ -3299,6 +2832,17 @@ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==" }, + "z-schema": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.5.tgz", + "integrity": "sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==", + "requires": { + "commander": "^9.4.1", + "lodash.get": "^4.4.2", + "lodash.isequal": "^4.5.0", + "validator": "^13.7.0" + } + }, "zod": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", From f1fcca471cb493e15b1563ade9d2c26456aab561 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Jul 2026 15:30:12 +0100 Subject: [PATCH 28/57] build(deps-dev): bump the migrator-dev group across 1 directory with 2 updates (#883) Bumps the migrator-dev group with 2 updates in the /migrator directory: [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) and [knip](https://github.com/webpro-nl/knip/tree/HEAD/packages/knip). Updates `@types/node` from 24.12.2 to 24.13.3 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) Updates `knip` from 6.16.1 to 6.27.0 - [Release notes](https://github.com/webpro-nl/knip/releases) - [Commits](https://github.com/webpro-nl/knip/commits/knip@6.27.0/packages/knip) --- updated-dependencies: - dependency-name: "@types/node" dependency-version: 24.13.2 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: migrator-dev - dependency-name: knip dependency-version: 6.21.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: migrator-dev ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- migrator/package-lock.json | 919 ++++++++++++++++++------------------- 1 file changed, 456 insertions(+), 463 deletions(-) diff --git a/migrator/package-lock.json b/migrator/package-lock.json index f16d0d77..e1807b9e 100644 --- a/migrator/package-lock.json +++ b/migrator/package-lock.json @@ -23,48 +23,44 @@ } }, "node_modules/@emnapi/core": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", - "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", + "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", "dev": true, - "license": "MIT", "optional": true, "dependencies": { - "@emnapi/wasi-threads": "1.2.1", + "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" } }, "node_modules/@emnapi/runtime": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", - "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", + "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", "dev": true, - "license": "MIT", "optional": true, "dependencies": { "tslib": "^2.4.0" } }, "node_modules/@emnapi/wasi-threads": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", - "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", + "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", "dev": true, - "license": "MIT", "optional": true, "dependencies": { "tslib": "^2.4.0" } }, "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", - "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", + "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==", "dev": true, - "license": "MIT", "optional": true, "dependencies": { - "@tybys/wasm-util": "^0.10.1" + "@tybys/wasm-util": "^0.10.3" }, "funding": { "type": "github", @@ -76,14 +72,13 @@ } }, "node_modules/@oxc-parser/binding-android-arm-eabi": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm-eabi/-/binding-android-arm-eabi-0.133.0.tgz", - "integrity": "sha512-l/44caGse+VpnY9gx0yvvc5QnnG3yG1FO3KZgYvNL1GZrfK86zIwAOgGEVlxDyRymzrU/KHiblPFpevKOmJmUA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm-eabi/-/binding-android-arm-eabi-0.137.0.tgz", + "integrity": "sha512-KDs+0VPdEmasOkpuJHW9V5WCF+cvYdMQv2Jd+aJXt+cxIx12NToRQRbXaRwUEDsZw+/jMk81Ve8ZFbjUkJTOwA==", "cpu": [ "arm" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "android" @@ -93,14 +88,13 @@ } }, "node_modules/@oxc-parser/binding-android-arm64": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm64/-/binding-android-arm64-0.133.0.tgz", - "integrity": "sha512-KUHmPMziLBp4u+zbrLdB7iWS7KshuZe+RAp7ELnY9SI9nNXBZ+dp8fiBqWOxhXqn+FQg3a4UcQhwmsJOKV8Jjg==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm64/-/binding-android-arm64-0.137.0.tgz", + "integrity": "sha512-WhALNzfy3x/RfC6bsqX+csavuUY0yHHE7XfgPE5M542uhoBZUUoGTPG+nkMbGoG4+gcfss5s7urMyn5QBHu0sw==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "android" @@ -110,14 +104,13 @@ } }, "node_modules/@oxc-parser/binding-darwin-arm64": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.133.0.tgz", - "integrity": "sha512-q8dWmnU/8ea2tga9w2f1PinQ5rcMPDUGkF64T189b65YMjUomET4oy5oRldOr4AwOQkneOG/Zttnz1Dvrc62wg==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.137.0.tgz", + "integrity": "sha512-bFPr5hgmNMOMoyPTGtdsK4Ug21RovIPojRMgDDhSp1LtCnc/DkLwGONKjgRjszg677RlGnkYSviQ8hHaUPOVYA==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "darwin" @@ -127,14 +120,13 @@ } }, "node_modules/@oxc-parser/binding-darwin-x64": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.133.0.tgz", - "integrity": "sha512-cOKeIELIB2bJnCKwqx4Rdj+1Lss/U6uCbLxRySZrhyOOQa1flKhwZFjEHRHxk8fU1NKmhK5OnTdPQ4CpjuFuVw==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.137.0.tgz", + "integrity": "sha512-CL5dMm1asqXIDZHg14FLxj3Mc36w8PI7xCWh1uA4is6z8g2XrIILoTcQYOxDbwzuk34RDPX5IAGUxZr6LA9KAg==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "darwin" @@ -144,14 +136,13 @@ } }, "node_modules/@oxc-parser/binding-freebsd-x64": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.133.0.tgz", - "integrity": "sha512-OpaSv4pW3KgFrMYQxTaS0aOE4T1DQF3qZE/4B6uqqv1KgPWWd4UQhJALi8PJPX1RRV5K7ThKXRfF7qGg2+3l1A==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.137.0.tgz", + "integrity": "sha512-79h8rYGnSlKPGWo7mHr2ixO6ea7aW8B0CT965SZ8SLbNnCOH5aOYBTeVXUY6eMvEaiLyWr8Skuiugr5pDYgLGw==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "freebsd" @@ -161,14 +152,13 @@ } }, "node_modules/@oxc-parser/binding-linux-arm-gnueabihf": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.133.0.tgz", - "integrity": "sha512-JGK1wlGrGwxBIlVSF7KWTX1/ru6BEtf28fRROztDRkLfiW+Kxa4onnriezMIiogfn9hVw2KzYcKiLjkLR2ns8A==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.137.0.tgz", + "integrity": "sha512-ASgmlSimhGyr0lksgVIo6hibz1obnDq4qJbiMX/AzltfgPnanRrzG1Q+23g8ljOHOjv6dsznkUuCYL3gg0sY1Q==", "cpu": [ "arm" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -178,14 +168,13 @@ } }, "node_modules/@oxc-parser/binding-linux-arm-musleabihf": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.133.0.tgz", - "integrity": "sha512-yuZO533Ftonxn/iyoqQzURzLQHMspvsIyfiCSNi1t/ER4eIQaR0SsmUOUm5b/lmSig7IWIUa5/BrbEkAPwcilQ==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.137.0.tgz", + "integrity": "sha512-AU2J9aa22Sx32wRGnDjybOU9TQXXQUud5sdUi+ZB0XxwM8aToWLweV+yA0wlQm0yIUVqljquqoHCYEq9II8gJQ==", "cpu": [ "arm" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -195,14 +184,13 @@ } }, "node_modules/@oxc-parser/binding-linux-arm64-gnu": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.133.0.tgz", - "integrity": "sha512-hvpbqT5pN2rR+3+xtWeizwfR/aZ0vGceg6TqYMl+ToxMpk9/tmnX7kSvQnfEUkoua8mhogzvIKsAkn0wxgblBA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.137.0.tgz", + "integrity": "sha512-GdEtiG89yMr7XkUGxifgodXEEm2f+xW2f9CpDjlgAnBOwhTmrpQMvhOGobLVKUyzf/qHBXW16smk5zbF3nZU6w==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -212,14 +200,13 @@ } }, "node_modules/@oxc-parser/binding-linux-arm64-musl": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.133.0.tgz", - "integrity": "sha512-wJQGamIosQBoJHW9+S5XxrtKRo3eyJxsnS1XCPrqN0LHi8uw1pTqqTfn3t/NVuvbBg7Pumn4ez9Eidgcn0xbEg==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.137.0.tgz", + "integrity": "sha512-EGJ+Bs8iXx8KBH8DQ5BLoEm5lnHaYjlh4/8j8vFhrr/6z4tqONy5BZDzLpKmmNWlN6Hlc5r8YOuBVHqZ9vRFEQ==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -229,14 +216,13 @@ } }, "node_modules/@oxc-parser/binding-linux-ppc64-gnu": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.133.0.tgz", - "integrity": "sha512-Koaz32/O5+abIfrNGdyndgRvdOZ9jEf5/z3Ep9h3h2QWpdDiUQpVwgH0OcMXCs+l9aXxPLtkupqyVig9W6FDKw==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.137.0.tgz", + "integrity": "sha512-vzFUQENy/fnbSe5DZWovq6tIBc1uhuMztanSW6rz1e9WdQE4gHwYuD7ZII6JnrJifd1R3RSoqiZbgRFlVL2tYQ==", "cpu": [ "ppc64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -246,14 +232,13 @@ } }, "node_modules/@oxc-parser/binding-linux-riscv64-gnu": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.133.0.tgz", - "integrity": "sha512-R4vOjWzxhnNWHnVLeiB6jNuIifdy9vcMXZGPc7StXcxBovI+U2zg1QhZ9o8OjV80oGivs1lX5NfPLzk4IPqlRA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.137.0.tgz", + "integrity": "sha512-SfVI14HBQs9gtLcUD5hTt5hsNbdrqSUNg9S8muN+LhVQ5nf1WwH3hAoK6B9NKgdYgWAQSXFXGiiBedQ4r/BKuw==", "cpu": [ "riscv64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -263,14 +248,13 @@ } }, "node_modules/@oxc-parser/binding-linux-riscv64-musl": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.133.0.tgz", - "integrity": "sha512-iwgBNUTHiMdxARLYuM0SBlnYeb19iw1Ea5M+4ERZupCsBMLArti6FyZ6UfFjJxIiTDr2oW2DGQFxlQVQ/dW9rA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.137.0.tgz", + "integrity": "sha512-e7Ppy4FCIFNQxT/ikSeIWFoQ0l+N9vgtRBtLcyZXeolTzApyVoPqEXsYPrcdM/9i0Bwk8knvYd37vaEMxHyi6g==", "cpu": [ "riscv64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -280,14 +264,13 @@ } }, "node_modules/@oxc-parser/binding-linux-s390x-gnu": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.133.0.tgz", - "integrity": "sha512-ZwZNo8FZmB/gVfboQl+wXilBigGl+6nQQs+nITOeAP/HcAOjiHl6XZJL9F/KXNEspODQcbjAiyjUbeCJd9a0fA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.137.0.tgz", + "integrity": "sha512-Bho5qFwdhqsIFR7gipYEUlqvi3SRrY8sugxXig380MIaakBB1PyU9+7dBiBVScfImTNWhijUxdBwqrprGdq5WA==", "cpu": [ "s390x" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -297,14 +280,13 @@ } }, "node_modules/@oxc-parser/binding-linux-x64-gnu": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.133.0.tgz", - "integrity": "sha512-govCvWx1dBlED3uu4qXctxpRcouu9I8Kn+DBktGCl760JtlGJzc9l/OmPJKlYWSbrRqKkMZehNeZ/4Wfma7uSA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.137.0.tgz", + "integrity": "sha512-36mGWtg7PyFzjJwGDkH6/F4o2nIDEoKXLPr/X/lwqklkomQwJJt1I5GJVmGhovUEmgPK5WAeAZMqlFCehwiy9Q==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -314,14 +296,13 @@ } }, "node_modules/@oxc-parser/binding-linux-x64-musl": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.133.0.tgz", - "integrity": "sha512-ssTlpXD5Mq9uCssDJPzlRWqBt4Y7Zzd9i+XZhWmK/9Y6KUIuAxVYTYiI8lxcGWi0+3/Cz4A8q9UrD4NK9Y2j7g==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.137.0.tgz", + "integrity": "sha512-/Jqx6+N7A44n2BdvUr7pXhVr2vFjs6WGH3unZRczwrfiH0H1zY0QwKQMG/dtRiTlKGDKGukznPT8lx84/oEsZg==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" @@ -331,14 +312,13 @@ } }, "node_modules/@oxc-parser/binding-openharmony-arm64": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-openharmony-arm64/-/binding-openharmony-arm64-0.133.0.tgz", - "integrity": "sha512-51aByfXhPtLEdWG4a2Ihdw6cPWV1ei1AarALpFdDP8MLWDLE2NuUMgbo3DERR2Kt8fT/ok1GUvBiLxVGke9uUQ==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-openharmony-arm64/-/binding-openharmony-arm64-0.137.0.tgz", + "integrity": "sha512-9Uj0qHNNl+OgT1UTGwF7ixIXU6T1u2SbMidmgPy/h1h/fl2gRS6YpAxxY1gwHofcWjoTwkoMFd8xs5Vuj6GOFA==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "openharmony" @@ -348,33 +328,31 @@ } }, "node_modules/@oxc-parser/binding-wasm32-wasi": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-wasm32-wasi/-/binding-wasm32-wasi-0.133.0.tgz", - "integrity": "sha512-2e16tkKp+wDO2GTAmXfxbBcCmGEaFPIJEIRBBmVKNVXSc8/fJsSIaBGyFTPHM9ST5GNWgJcYIt94rDTks+PLwA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-wasm32-wasi/-/binding-wasm32-wasi-0.137.0.tgz", + "integrity": "sha512-gW2vfkytNGgMVADiuzdvOfw0mWG9za20F/1fCJsif5aBMAvWJTSbpIXbIe0XkOe0VENk+PadpQ7cZgUy2sUJcA==", "cpu": [ "wasm32" ], "dev": true, - "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "1.10.0", - "@emnapi/runtime": "1.10.0", - "@napi-rs/wasm-runtime": "^1.1.4" + "@emnapi/core": "1.11.1", + "@emnapi/runtime": "1.11.1", + "@napi-rs/wasm-runtime": "^1.1.5" }, "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@oxc-parser/binding-win32-arm64-msvc": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.133.0.tgz", - "integrity": "sha512-KPTNDKbxH1cglrqTyVeXHb4Pk4oksz8EcE1/v8zqU7N4UXbiHfA/IwtXZ2U77fnRAWBbgVkl/lZbL7o3hRdejg==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.137.0.tgz", + "integrity": "sha512-x+pFANF0yL5uK/6T7lu6SlR5qid6sp//eZXKLq5iNsIE+EQg6EaS8/wsW7E91nXXjpnPhSoMOHXShSVhGRdn8w==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" @@ -384,14 +362,13 @@ } }, "node_modules/@oxc-parser/binding-win32-ia32-msvc": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.133.0.tgz", - "integrity": "sha512-Una1bNYv9zCavQrfnDR9wuZVB3itLjCEH4Oz7i6CwAJN/Xq9b+zbbcxmvdkKvvJt4Ngc/MBmIYlbLo3zS4TQ0A==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.137.0.tgz", + "integrity": "sha512-sQUqym80PFi6McRsIqfJrSu2JrSClEZIXXD+/FjAFoULEKzOPsldIdFBG96xdX8aVMzCNQ9792FPx3MfkEIrFA==", "cpu": [ "ia32" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" @@ -401,14 +378,13 @@ } }, "node_modules/@oxc-parser/binding-win32-x64-msvc": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.133.0.tgz", - "integrity": "sha512-kjBhCiOGSYTwDJQuuZa7a94JbP8htWu7J0X1KwH74kV2K5eYf6eyJRYmkpCDvr0XEL8tMxYI4WU1VekblFCLgg==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.137.0.tgz", + "integrity": "sha512-2AsevxlvNN4WKxpEn3RtqD5zbqMaXF+T7JXblsP4gVuY+vC9dXS4ED/PwfRCliFqoeisYS3Iro4DHzxr0TEvVA==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" @@ -418,281 +394,282 @@ } }, "node_modules/@oxc-project/types": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz", - "integrity": "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.137.0.tgz", + "integrity": "sha512-WT+Gb24i8hmvo85AIv2oEYouEXkRlKAlT9WaCa3TfLgNCN+GhrJOGZuIlMouAh38Qe4QOx26eUOVsq70qXrywA==", "dev": true, - "license": "MIT", "funding": { "url": "https://github.com/sponsors/Boshen" } }, "node_modules/@oxc-resolver/binding-android-arm-eabi": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm-eabi/-/binding-android-arm-eabi-11.20.0.tgz", - "integrity": "sha512-IjfWOXRgJFNdORDl+Uf1aibNgZY2guOD3zmOhx1BGVb/MIiqlFTdmjpQNplSN58lhWehnX4UNqC3QwpUo8pjJg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm-eabi/-/binding-android-arm-eabi-11.21.3.tgz", + "integrity": "sha512-eNU11A2WNizh04v3uyaJCootrHIaS0B9aHYXvAvVnPNk4xYSjMUjHnhQ6dewPN2MRYDskV85d1N0Aw0WNWhcyg==", "cpu": [ "arm" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@oxc-resolver/binding-android-arm64": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm64/-/binding-android-arm64-11.20.0.tgz", - "integrity": "sha512-QqslZAuFQG8Q9xm7JuIn8JUbvywhSBMVhuQHtYW+auirZJloS41oxUUaBXk7uUhZJgp44c5zQLeVvmFaDQB+2Q==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm64/-/binding-android-arm64-11.21.3.tgz", + "integrity": "sha512-8Q+ZjTLvn2dIcWsrmhdrEihm7q+ag/k+mkry7Z+t0QbbHaVxXQfvH9AewyVMh/WrpEKhQ3DDgx9fYbqeCpeOEw==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@oxc-resolver/binding-darwin-arm64": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-arm64/-/binding-darwin-arm64-11.20.0.tgz", - "integrity": "sha512-MUcavykj2ewlR+kc5arpg4tC2RvzJkUxWtNv74pf7lcNk00GpIpN43vXMj+j6r4eMmfZhlb8hueKoIb8e9kAGQ==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-arm64/-/binding-darwin-arm64-11.21.3.tgz", + "integrity": "sha512-wkh0qKZGHXVUDxFw3oA1TXnU2BDYY/r775oJflGeIr8uDPPoN2pk8gijQIzYRT6hoql/lg3+Tx/SaTn9e2/aGg==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@oxc-resolver/binding-darwin-x64": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-x64/-/binding-darwin-x64-11.20.0.tgz", - "integrity": "sha512-BGB16nRUK5Etiv//ihPyzj8Lj1px0mhh4YIfe0FDf045ywknfSm0GEbiRESpr6Q4K82AvnyaRIhhluHByvS4bg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-x64/-/binding-darwin-x64-11.21.3.tgz", + "integrity": "sha512-HbNc23FAQYbuyDV2vBWMez4u4mrsm5RAkniGZAWqr6lYZ3N4beeqIb776jzwRl8qL2zRhHVXpUj97X0QgogVzg==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@oxc-resolver/binding-freebsd-x64": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-freebsd-x64/-/binding-freebsd-x64-11.20.0.tgz", - "integrity": "sha512-JZgtePaqj3qmD5XFHJaSLWzHRxQu0LaPkdoM1KJXYADvAaa83ijXHclV3ej3CueeW0wxfIAbGCZVP45J0CA7uQ==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-freebsd-x64/-/binding-freebsd-x64-11.21.3.tgz", + "integrity": "sha512-K6xNsTUPEUdfrn0+kbMq5nOUB5w1C5pavPQngt4TM2FpN91lP0PBe2srSpamb4d69O7h86oAi/qWX/kZNRSjkw==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "freebsd" ] }, "node_modules/@oxc-resolver/binding-linux-arm-gnueabihf": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-11.20.0.tgz", - "integrity": "sha512-hOQ/p3ry3v3SchUBXicrrnszaI/UmYzM4wtS4RGfwgVUX7a+HbyQSzJ5aOzu+o6XZkFkS3ZXN4PZAzhOb77OSg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-11.21.3.tgz", + "integrity": "sha512-VcFmOpcpWX1zoEy8M58tR2M9YxM+Z9RuQhqAx5q0CTmrruaP7Gveejg75hzd/5sg5nk9G3aLALEa3hE2FsmmTQ==", "cpu": [ "arm" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@oxc-resolver/binding-linux-arm-musleabihf": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-11.20.0.tgz", - "integrity": "sha512-2ArPksaw0AqeuGBfoS715VF+JvJQAhD2niWgjE5hVO+L+nAfikVQopvngCMX9x4BD8itWoQ3dnikrQyl5Ho5Jg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-11.21.3.tgz", + "integrity": "sha512-quVoxFLBy43hWaQbbDtQNRwAX5vX76mv7n64icAtQcJ3eNgVeblqmkupF/hAneNthdqSlnd1sTjb3aQSaDPaCQ==", "cpu": [ "arm" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@oxc-resolver/binding-linux-arm64-gnu": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-11.20.0.tgz", - "integrity": "sha512-0bJnmYFp62JdZ4nVMDUZ/C58BCZOCcqgKtnUlp7L9Ojf/czIN+3j72YlLPeWLkzlr6SlYvIQA4SGV/HyO0d+qg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-11.21.3.tgz", + "integrity": "sha512-X0AqNZgcD07Q4V3RDK18/vYOj/HQT/FnmEFGYS2jTWqY7JO13ryE3TEs3eAIgUJhBnNkpEaiXqz3VK8M7qQhWQ==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@oxc-resolver/binding-linux-arm64-musl": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-musl/-/binding-linux-arm64-musl-11.20.0.tgz", - "integrity": "sha512-wKHHzPKZo7Ufhv/Bt6yxT7FOgnIgW4gwXcJUipkShGp68W3wGVqvr1Sr0fY65lN0Oy6y41+g2kIDvkgZaMMUkw==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-musl/-/binding-linux-arm64-musl-11.21.3.tgz", + "integrity": "sha512-YkaQnaKYdbuaXvRt5Qd0GpbihzVnyfR6z1SpYfIUC6RTu4NF7lDKPjVkYb+jRI2gedVO2rVpN35Y6akG6ud4Lw==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@oxc-resolver/binding-linux-ppc64-gnu": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-11.20.0.tgz", - "integrity": "sha512-RN8goF7Ie0B79L4i4G6OeBocTgSC56vJbQ65VJje+oXnldVpLnOU7j/AQ/dP94TcCS+Yh6WG8u3Qt4ETteXFNQ==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-11.21.3.tgz", + "integrity": "sha512-gB9HwhrPiFqUzDeEq+y/CgAijz1YdI6BnXz5GaH2Pa9cWdutchlkGFAiAuGb/PjVQpiK6NFKzFuztxrweoit7A==", "cpu": [ "ppc64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@oxc-resolver/binding-linux-riscv64-gnu": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-11.20.0.tgz", - "integrity": "sha512-5l1yU6/xQEqLZRzxqmMxJfWPslpwCmBsdDGaBvABPehxquCXDC7dd7oraNdKSJUMDXSM7VvVj8H2D2FTjU7oWw==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-11.21.3.tgz", + "integrity": "sha512-zjDWBlYk8QGv0H8dsPUWqkfjYIIjG2TvspGkzXL0eImbgxtZorA/klKeHyolevoT3Kvbi+1iMr9Lhrh7jf54Og==", "cpu": [ "riscv64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@oxc-resolver/binding-linux-riscv64-musl": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-11.20.0.tgz", - "integrity": "sha512-xHEvkbgz6UC+A3JOyDQy76LkUaxsNSfIr3/GV8slwZsnuooJiIB34gzJfsyvR4JdCYNUUPsRJc/w/oWkODu+hg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-11.21.3.tgz", + "integrity": "sha512-4UfsQvacV388y1zpXL7C1x1FNYaV52JtuNRiuzrfQA2z1z6ElVrsidkGsrvQ5EgeSq1Pj7kaKqrgGkvFuxJ/tw==", "cpu": [ "riscv64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@oxc-resolver/binding-linux-s390x-gnu": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-11.20.0.tgz", - "integrity": "sha512-aWPDUUmSeyHvlW+SoEUd+JIJsQhVhu6a5tBpDRMu058naPAchTgAVGCFy35zjbnFlt0i8hLWziff6HX0D3LU4g==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-11.21.3.tgz", + "integrity": "sha512-b5uH+HKH0MP5mNBYaK75SKsJbw52URqrx2LavYdq6wb0l3ExAG5niYRP9DWUNHdKilpaBVM2bXk9HNWrH3ew7Q==", "cpu": [ "s390x" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@oxc-resolver/binding-linux-x64-gnu": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-gnu/-/binding-linux-x64-gnu-11.20.0.tgz", - "integrity": "sha512-x2YeSimvhJjKLVD8KSu8f/rqU1potcdEMkApIPJqjZWN7c2Fpt4g2X32WDg1p+XDAmyT7nuQGe0vnhvXeLbH+g==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-gnu/-/binding-linux-x64-gnu-11.21.3.tgz", + "integrity": "sha512-PjYlmilBpNRh2ntXNYAK3Am5w/nPfEpnU/96iNx7CI8EzAn12J4JRiec63wHJTH31nLoCNxBg/829pN+3CfG3Q==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@oxc-resolver/binding-linux-x64-musl": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-musl/-/binding-linux-x64-musl-11.20.0.tgz", - "integrity": "sha512-kcRLEIxpZefeYfLChjpgFf3ilBzRDZ+yobMrpRsQlSrxuFGtm3U6PMU7AaEpMqo3NfDGVyJJseAjnRLzMFHjwQ==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-musl/-/binding-linux-x64-musl-11.21.3.tgz", + "integrity": "sha512-QTBAb7JuHlZ7JUEyM8UiQi2f7m/L4swBhP2TNpYIDc9Wp/wRw1G/8sl6i13aIzQAXH7LKIm294LeOHd0lQR8zA==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@oxc-resolver/binding-openharmony-arm64": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-openharmony-arm64/-/binding-openharmony-arm64-11.20.0.tgz", - "integrity": "sha512-HHcfnApSZGtKhTiHqe8OZruOZe5XuFQH5/E0Yhj3u8fnFvzkM4/k6WjacUf4SvA0SPEAbfbgYmVPuo0VX/fIBQ==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-openharmony-arm64/-/binding-openharmony-arm64-11.21.3.tgz", + "integrity": "sha512-4j1DFwjwv36ec9kds0jU/ucQ5Ha4ERO/H95BxR5JFf0kqUUAJ1kwII7XhTc1vZrkdJkvLGC9Q2MbpObpum8RBg==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "openharmony" ] }, "node_modules/@oxc-resolver/binding-wasm32-wasi": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-wasm32-wasi/-/binding-wasm32-wasi-11.20.0.tgz", - "integrity": "sha512-Tn0y1XOFYHNfK1wp1Z5QK8Rcld/bsOwRISQXfqAZ5IBpv8Gz1IvV39fUWNprqNdRizgcvFhOzWwFun2zkJsyBg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-wasm32-wasi/-/binding-wasm32-wasi-11.21.3.tgz", + "integrity": "sha512-i8oluoel5kru/j1WNrjmQSiA3GQ7wvIYVR1IwIoZtKogAhya2iub+ZKIeSIkcJOrnzQ18Tzl/F+kL3fYOxZLvA==", "cpu": [ "wasm32" ], "dev": true, - "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "1.10.0", - "@emnapi/runtime": "1.10.0", - "@napi-rs/wasm-runtime": "^1.1.4" + "@emnapi/core": "1.11.0", + "@emnapi/runtime": "1.11.0", + "@napi-rs/wasm-runtime": "^1.1.5" }, "engines": { "node": ">=14.0.0" } }, + "node_modules/@oxc-resolver/binding-wasm32-wasi/node_modules/@emnapi/core": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.0.tgz", + "integrity": "sha512-l9Oo58x0HOP5znGzVhYW9U3e5wVuA4LAZU2AGezTmkhO1CgQRFDhDg4nneHsu/t3WniXg9QrG2nIXL/ZS8ln8Q==", + "dev": true, + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@oxc-resolver/binding-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.0.tgz", + "integrity": "sha512-55coeOFKHv1ywEcUXJtWU5f+Jr/W5tZDvZig8DLKSwUN1JpROQ4rk/SNOQiFWmaR/VKF4zuFyW1B8JduOSv6Pg==", + "dev": true, + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@oxc-resolver/binding-win32-arm64-msvc": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-11.20.0.tgz", - "integrity": "sha512-qPi25YNPe4YenS8MgsQU2+bIFHxxpLx1LVna2444cEHqNPhNjvWf9zqj4aWE43H9LpAsTmkkAlA3eL5ElBU3mA==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-11.21.3.tgz", + "integrity": "sha512-M/8dw8dD6aOs+NlPJax401CZB9I7Aut84isQLgALGGwke4Afvw+/7yYhZb94yXf6t2sPLhQLmSmtSV+2FhsOWg==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@oxc-resolver/binding-win32-x64-msvc": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-x64-msvc/-/binding-win32-x64-msvc-11.20.0.tgz", - "integrity": "sha512-Wb14jWEW8huH6It9F6sXd9vrYmIS7pMrgkU6sxpLxkP+9z+wRgs71hUEhRpcn8FOXAFa27FVWfY2tRpbfTzfLw==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-x64-msvc/-/binding-win32-x64-msvc-11.21.3.tgz", + "integrity": "sha512-H7BCt/VnS9hnmMp42eGhZ99izSCRvlnWwy/N71K1/J8QoExwY4262Z8QiEkMDtduRJrztayDxETTckmUuAVL9Q==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" @@ -753,11 +730,10 @@ "integrity": "sha512-cka47fVSo6lfQDIATYqb/vO1nvFfbPw7uWLayIXIhGETj0wcOOlrlkobOMDNQOFr9QOafegUPq13V2+6vtD7yg==" }, "node_modules/@tybys/wasm-util": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", - "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", "dev": true, - "license": "MIT", "optional": true, "dependencies": { "tslib": "^2.4.0" @@ -782,11 +758,11 @@ "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==" }, "node_modules/@types/node": { - "version": "24.12.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.2.tgz", - "integrity": "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g==", + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.3.tgz", + "integrity": "sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==", "dependencies": { - "undici-types": "~7.16.0" + "undici-types": "~7.18.0" } }, "node_modules/@types/validator": { @@ -1108,9 +1084,9 @@ } }, "node_modules/knip": { - "version": "6.16.1", - "resolved": "https://registry.npmjs.org/knip/-/knip-6.16.1.tgz", - "integrity": "sha512-TKMn1rxgH6h9vXR9Y0B+Cq7AdPTr9EI02IwoT65NzqYUkvoDQAaJ/aPybiFpAhZ1px6cNYYwXf86iHkBgzCo9w==", + "version": "6.27.0", + "resolved": "https://registry.npmjs.org/knip/-/knip-6.27.0.tgz", + "integrity": "sha512-CngYEYrD0n20N06FXA8n3u/0Wnnugoa+B9k14OP+iKIgkCHuzvIdsP3nfwjhByoc1WfogpxfiriMboAXFETDUw==", "dev": true, "funding": [ { @@ -1122,19 +1098,18 @@ "url": "https://opencollective.com/knip" } ], - "license": "ISC", "dependencies": { "fdir": "^6.5.0", "formatly": "^0.3.0", "get-tsconfig": "4.14.0", "jiti": "^2.7.0", - "oxc-parser": "^0.133.0", - "oxc-resolver": "^11.20.0", + "oxc-parser": "^0.137.0", + "oxc-resolver": "11.21.3", "picomatch": "^4.0.4", "smol-toml": "^1.6.1", "strip-json-comments": "5.0.3", - "tinyglobby": "^0.2.16", - "unbash": "^3.0.0", + "tinyglobby": "^0.2.17", + "unbash": "^4.0.1", "yaml": "^2.9.0", "zod": "^4.1.11" }, @@ -1237,13 +1212,12 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/oxc-parser": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/oxc-parser/-/oxc-parser-0.133.0.tgz", - "integrity": "sha512-661RSx+ZcjBmjBYid+Fpp/2F5EbtildpeoZh5HdgnGs+jZ03nqQEQW8yGkt4BGyOC3OMPDQQRl8M5kqD2/g6jw==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/oxc-parser/-/oxc-parser-0.137.0.tgz", + "integrity": "sha512-yFImD+WLElJpLKy8llG1qe4DCmMsL18peRp8XP1JKfig/gISbJkglnpDtX2aTmAn10kZF7164HbN2H8QPsXxGg==", "dev": true, - "license": "MIT", "dependencies": { - "@oxc-project/types": "^0.133.0" + "@oxc-project/types": "^0.137.0" }, "engines": { "node": "^20.19.0 || >=22.12.0" @@ -1252,57 +1226,56 @@ "url": "https://github.com/sponsors/Boshen" }, "optionalDependencies": { - "@oxc-parser/binding-android-arm-eabi": "0.133.0", - "@oxc-parser/binding-android-arm64": "0.133.0", - "@oxc-parser/binding-darwin-arm64": "0.133.0", - "@oxc-parser/binding-darwin-x64": "0.133.0", - "@oxc-parser/binding-freebsd-x64": "0.133.0", - "@oxc-parser/binding-linux-arm-gnueabihf": "0.133.0", - "@oxc-parser/binding-linux-arm-musleabihf": "0.133.0", - "@oxc-parser/binding-linux-arm64-gnu": "0.133.0", - "@oxc-parser/binding-linux-arm64-musl": "0.133.0", - "@oxc-parser/binding-linux-ppc64-gnu": "0.133.0", - "@oxc-parser/binding-linux-riscv64-gnu": "0.133.0", - "@oxc-parser/binding-linux-riscv64-musl": "0.133.0", - "@oxc-parser/binding-linux-s390x-gnu": "0.133.0", - "@oxc-parser/binding-linux-x64-gnu": "0.133.0", - "@oxc-parser/binding-linux-x64-musl": "0.133.0", - "@oxc-parser/binding-openharmony-arm64": "0.133.0", - "@oxc-parser/binding-wasm32-wasi": "0.133.0", - "@oxc-parser/binding-win32-arm64-msvc": "0.133.0", - "@oxc-parser/binding-win32-ia32-msvc": "0.133.0", - "@oxc-parser/binding-win32-x64-msvc": "0.133.0" + "@oxc-parser/binding-android-arm-eabi": "0.137.0", + "@oxc-parser/binding-android-arm64": "0.137.0", + "@oxc-parser/binding-darwin-arm64": "0.137.0", + "@oxc-parser/binding-darwin-x64": "0.137.0", + "@oxc-parser/binding-freebsd-x64": "0.137.0", + "@oxc-parser/binding-linux-arm-gnueabihf": "0.137.0", + "@oxc-parser/binding-linux-arm-musleabihf": "0.137.0", + "@oxc-parser/binding-linux-arm64-gnu": "0.137.0", + "@oxc-parser/binding-linux-arm64-musl": "0.137.0", + "@oxc-parser/binding-linux-ppc64-gnu": "0.137.0", + "@oxc-parser/binding-linux-riscv64-gnu": "0.137.0", + "@oxc-parser/binding-linux-riscv64-musl": "0.137.0", + "@oxc-parser/binding-linux-s390x-gnu": "0.137.0", + "@oxc-parser/binding-linux-x64-gnu": "0.137.0", + "@oxc-parser/binding-linux-x64-musl": "0.137.0", + "@oxc-parser/binding-openharmony-arm64": "0.137.0", + "@oxc-parser/binding-wasm32-wasi": "0.137.0", + "@oxc-parser/binding-win32-arm64-msvc": "0.137.0", + "@oxc-parser/binding-win32-ia32-msvc": "0.137.0", + "@oxc-parser/binding-win32-x64-msvc": "0.137.0" } }, "node_modules/oxc-resolver": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/oxc-resolver/-/oxc-resolver-11.20.0.tgz", - "integrity": "sha512-CblytBiV/a/ZXY34dsVU2NxhIOxMXst8CvDCtyBelVITgd7PLrKzbEbA6oKLdPjvDKDzCiW48qzmzZ+mYaqn+g==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/oxc-resolver/-/oxc-resolver-11.21.3.tgz", + "integrity": "sha512-2Mx3fKQz7+xgrBONjsxOgCGtMHOn38/HxMzW1I5efwXB5a4lRN0Vp40gYUJFBWJslcrvwoofTrqoTnLbwTd3pA==", "dev": true, - "license": "MIT", "funding": { "url": "https://github.com/sponsors/Boshen" }, "optionalDependencies": { - "@oxc-resolver/binding-android-arm-eabi": "11.20.0", - "@oxc-resolver/binding-android-arm64": "11.20.0", - "@oxc-resolver/binding-darwin-arm64": "11.20.0", - "@oxc-resolver/binding-darwin-x64": "11.20.0", - "@oxc-resolver/binding-freebsd-x64": "11.20.0", - "@oxc-resolver/binding-linux-arm-gnueabihf": "11.20.0", - "@oxc-resolver/binding-linux-arm-musleabihf": "11.20.0", - "@oxc-resolver/binding-linux-arm64-gnu": "11.20.0", - "@oxc-resolver/binding-linux-arm64-musl": "11.20.0", - "@oxc-resolver/binding-linux-ppc64-gnu": "11.20.0", - "@oxc-resolver/binding-linux-riscv64-gnu": "11.20.0", - "@oxc-resolver/binding-linux-riscv64-musl": "11.20.0", - "@oxc-resolver/binding-linux-s390x-gnu": "11.20.0", - "@oxc-resolver/binding-linux-x64-gnu": "11.20.0", - "@oxc-resolver/binding-linux-x64-musl": "11.20.0", - "@oxc-resolver/binding-openharmony-arm64": "11.20.0", - "@oxc-resolver/binding-wasm32-wasi": "11.20.0", - "@oxc-resolver/binding-win32-arm64-msvc": "11.20.0", - "@oxc-resolver/binding-win32-x64-msvc": "11.20.0" + "@oxc-resolver/binding-android-arm-eabi": "11.21.3", + "@oxc-resolver/binding-android-arm64": "11.21.3", + "@oxc-resolver/binding-darwin-arm64": "11.21.3", + "@oxc-resolver/binding-darwin-x64": "11.21.3", + "@oxc-resolver/binding-freebsd-x64": "11.21.3", + "@oxc-resolver/binding-linux-arm-gnueabihf": "11.21.3", + "@oxc-resolver/binding-linux-arm-musleabihf": "11.21.3", + "@oxc-resolver/binding-linux-arm64-gnu": "11.21.3", + "@oxc-resolver/binding-linux-arm64-musl": "11.21.3", + "@oxc-resolver/binding-linux-ppc64-gnu": "11.21.3", + "@oxc-resolver/binding-linux-riscv64-gnu": "11.21.3", + "@oxc-resolver/binding-linux-riscv64-musl": "11.21.3", + "@oxc-resolver/binding-linux-s390x-gnu": "11.21.3", + "@oxc-resolver/binding-linux-x64-gnu": "11.21.3", + "@oxc-resolver/binding-linux-x64-musl": "11.21.3", + "@oxc-resolver/binding-openharmony-arm64": "11.21.3", + "@oxc-resolver/binding-wasm32-wasi": "11.21.3", + "@oxc-resolver/binding-win32-arm64-msvc": "11.21.3", + "@oxc-resolver/binding-win32-x64-msvc": "11.21.3" } }, "node_modules/path-parse": { @@ -1592,7 +1565,6 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "dev": true, - "license": "0BSD", "optional": true }, "node_modules/type-fest": { @@ -1636,20 +1608,18 @@ } }, "node_modules/unbash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unbash/-/unbash-3.0.0.tgz", - "integrity": "sha512-FeFPZ/WFT0mbRCuydiZzpPFlrYN8ZUpphQKoq4EeElVIYjYyGzPMxQR/simUwCOJIyVhpFk4RbtyO7RuMpMnHA==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/unbash/-/unbash-4.0.4.tgz", + "integrity": "sha512-60m9IVGbavD6jholbxt0jVBXZkEB/HsMZq7Tyaghseve2/Sf0zQRAIfWsD34sde+DKP2tBxJS2wP88ZM0D1FhA==", "dev": true, - "license": "ISC", "engines": { "node": ">=14" } }, "node_modules/undici-types": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", - "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", - "license": "MIT" + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==" }, "node_modules/universalify": { "version": "0.1.2", @@ -1800,20 +1770,20 @@ }, "dependencies": { "@emnapi/core": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", - "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", + "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", "dev": true, "optional": true, "requires": { - "@emnapi/wasi-threads": "1.2.1", + "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" } }, "@emnapi/runtime": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", - "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", + "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", "dev": true, "optional": true, "requires": { @@ -1821,9 +1791,9 @@ } }, "@emnapi/wasi-threads": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", - "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", + "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", "dev": true, "optional": true, "requires": { @@ -1831,301 +1801,324 @@ } }, "@napi-rs/wasm-runtime": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", - "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", + "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==", "dev": true, "optional": true, "requires": { - "@tybys/wasm-util": "^0.10.1" + "@tybys/wasm-util": "^0.10.3" } }, "@oxc-parser/binding-android-arm-eabi": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm-eabi/-/binding-android-arm-eabi-0.133.0.tgz", - "integrity": "sha512-l/44caGse+VpnY9gx0yvvc5QnnG3yG1FO3KZgYvNL1GZrfK86zIwAOgGEVlxDyRymzrU/KHiblPFpevKOmJmUA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm-eabi/-/binding-android-arm-eabi-0.137.0.tgz", + "integrity": "sha512-KDs+0VPdEmasOkpuJHW9V5WCF+cvYdMQv2Jd+aJXt+cxIx12NToRQRbXaRwUEDsZw+/jMk81Ve8ZFbjUkJTOwA==", "dev": true, "optional": true }, "@oxc-parser/binding-android-arm64": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm64/-/binding-android-arm64-0.133.0.tgz", - "integrity": "sha512-KUHmPMziLBp4u+zbrLdB7iWS7KshuZe+RAp7ELnY9SI9nNXBZ+dp8fiBqWOxhXqn+FQg3a4UcQhwmsJOKV8Jjg==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm64/-/binding-android-arm64-0.137.0.tgz", + "integrity": "sha512-WhALNzfy3x/RfC6bsqX+csavuUY0yHHE7XfgPE5M542uhoBZUUoGTPG+nkMbGoG4+gcfss5s7urMyn5QBHu0sw==", "dev": true, "optional": true }, "@oxc-parser/binding-darwin-arm64": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.133.0.tgz", - "integrity": "sha512-q8dWmnU/8ea2tga9w2f1PinQ5rcMPDUGkF64T189b65YMjUomET4oy5oRldOr4AwOQkneOG/Zttnz1Dvrc62wg==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.137.0.tgz", + "integrity": "sha512-bFPr5hgmNMOMoyPTGtdsK4Ug21RovIPojRMgDDhSp1LtCnc/DkLwGONKjgRjszg677RlGnkYSviQ8hHaUPOVYA==", "dev": true, "optional": true }, "@oxc-parser/binding-darwin-x64": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.133.0.tgz", - "integrity": "sha512-cOKeIELIB2bJnCKwqx4Rdj+1Lss/U6uCbLxRySZrhyOOQa1flKhwZFjEHRHxk8fU1NKmhK5OnTdPQ4CpjuFuVw==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.137.0.tgz", + "integrity": "sha512-CL5dMm1asqXIDZHg14FLxj3Mc36w8PI7xCWh1uA4is6z8g2XrIILoTcQYOxDbwzuk34RDPX5IAGUxZr6LA9KAg==", "dev": true, "optional": true }, "@oxc-parser/binding-freebsd-x64": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.133.0.tgz", - "integrity": "sha512-OpaSv4pW3KgFrMYQxTaS0aOE4T1DQF3qZE/4B6uqqv1KgPWWd4UQhJALi8PJPX1RRV5K7ThKXRfF7qGg2+3l1A==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.137.0.tgz", + "integrity": "sha512-79h8rYGnSlKPGWo7mHr2ixO6ea7aW8B0CT965SZ8SLbNnCOH5aOYBTeVXUY6eMvEaiLyWr8Skuiugr5pDYgLGw==", "dev": true, "optional": true }, "@oxc-parser/binding-linux-arm-gnueabihf": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.133.0.tgz", - "integrity": "sha512-JGK1wlGrGwxBIlVSF7KWTX1/ru6BEtf28fRROztDRkLfiW+Kxa4onnriezMIiogfn9hVw2KzYcKiLjkLR2ns8A==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.137.0.tgz", + "integrity": "sha512-ASgmlSimhGyr0lksgVIo6hibz1obnDq4qJbiMX/AzltfgPnanRrzG1Q+23g8ljOHOjv6dsznkUuCYL3gg0sY1Q==", "dev": true, "optional": true }, "@oxc-parser/binding-linux-arm-musleabihf": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.133.0.tgz", - "integrity": "sha512-yuZO533Ftonxn/iyoqQzURzLQHMspvsIyfiCSNi1t/ER4eIQaR0SsmUOUm5b/lmSig7IWIUa5/BrbEkAPwcilQ==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.137.0.tgz", + "integrity": "sha512-AU2J9aa22Sx32wRGnDjybOU9TQXXQUud5sdUi+ZB0XxwM8aToWLweV+yA0wlQm0yIUVqljquqoHCYEq9II8gJQ==", "dev": true, "optional": true }, "@oxc-parser/binding-linux-arm64-gnu": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.133.0.tgz", - "integrity": "sha512-hvpbqT5pN2rR+3+xtWeizwfR/aZ0vGceg6TqYMl+ToxMpk9/tmnX7kSvQnfEUkoua8mhogzvIKsAkn0wxgblBA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.137.0.tgz", + "integrity": "sha512-GdEtiG89yMr7XkUGxifgodXEEm2f+xW2f9CpDjlgAnBOwhTmrpQMvhOGobLVKUyzf/qHBXW16smk5zbF3nZU6w==", "dev": true, "optional": true }, "@oxc-parser/binding-linux-arm64-musl": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.133.0.tgz", - "integrity": "sha512-wJQGamIosQBoJHW9+S5XxrtKRo3eyJxsnS1XCPrqN0LHi8uw1pTqqTfn3t/NVuvbBg7Pumn4ez9Eidgcn0xbEg==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.137.0.tgz", + "integrity": "sha512-EGJ+Bs8iXx8KBH8DQ5BLoEm5lnHaYjlh4/8j8vFhrr/6z4tqONy5BZDzLpKmmNWlN6Hlc5r8YOuBVHqZ9vRFEQ==", "dev": true, "optional": true }, "@oxc-parser/binding-linux-ppc64-gnu": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.133.0.tgz", - "integrity": "sha512-Koaz32/O5+abIfrNGdyndgRvdOZ9jEf5/z3Ep9h3h2QWpdDiUQpVwgH0OcMXCs+l9aXxPLtkupqyVig9W6FDKw==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.137.0.tgz", + "integrity": "sha512-vzFUQENy/fnbSe5DZWovq6tIBc1uhuMztanSW6rz1e9WdQE4gHwYuD7ZII6JnrJifd1R3RSoqiZbgRFlVL2tYQ==", "dev": true, "optional": true }, "@oxc-parser/binding-linux-riscv64-gnu": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.133.0.tgz", - "integrity": "sha512-R4vOjWzxhnNWHnVLeiB6jNuIifdy9vcMXZGPc7StXcxBovI+U2zg1QhZ9o8OjV80oGivs1lX5NfPLzk4IPqlRA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.137.0.tgz", + "integrity": "sha512-SfVI14HBQs9gtLcUD5hTt5hsNbdrqSUNg9S8muN+LhVQ5nf1WwH3hAoK6B9NKgdYgWAQSXFXGiiBedQ4r/BKuw==", "dev": true, "optional": true }, "@oxc-parser/binding-linux-riscv64-musl": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.133.0.tgz", - "integrity": "sha512-iwgBNUTHiMdxARLYuM0SBlnYeb19iw1Ea5M+4ERZupCsBMLArti6FyZ6UfFjJxIiTDr2oW2DGQFxlQVQ/dW9rA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.137.0.tgz", + "integrity": "sha512-e7Ppy4FCIFNQxT/ikSeIWFoQ0l+N9vgtRBtLcyZXeolTzApyVoPqEXsYPrcdM/9i0Bwk8knvYd37vaEMxHyi6g==", "dev": true, "optional": true }, "@oxc-parser/binding-linux-s390x-gnu": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.133.0.tgz", - "integrity": "sha512-ZwZNo8FZmB/gVfboQl+wXilBigGl+6nQQs+nITOeAP/HcAOjiHl6XZJL9F/KXNEspODQcbjAiyjUbeCJd9a0fA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.137.0.tgz", + "integrity": "sha512-Bho5qFwdhqsIFR7gipYEUlqvi3SRrY8sugxXig380MIaakBB1PyU9+7dBiBVScfImTNWhijUxdBwqrprGdq5WA==", "dev": true, "optional": true }, "@oxc-parser/binding-linux-x64-gnu": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.133.0.tgz", - "integrity": "sha512-govCvWx1dBlED3uu4qXctxpRcouu9I8Kn+DBktGCl760JtlGJzc9l/OmPJKlYWSbrRqKkMZehNeZ/4Wfma7uSA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.137.0.tgz", + "integrity": "sha512-36mGWtg7PyFzjJwGDkH6/F4o2nIDEoKXLPr/X/lwqklkomQwJJt1I5GJVmGhovUEmgPK5WAeAZMqlFCehwiy9Q==", "dev": true, "optional": true }, "@oxc-parser/binding-linux-x64-musl": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.133.0.tgz", - "integrity": "sha512-ssTlpXD5Mq9uCssDJPzlRWqBt4Y7Zzd9i+XZhWmK/9Y6KUIuAxVYTYiI8lxcGWi0+3/Cz4A8q9UrD4NK9Y2j7g==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.137.0.tgz", + "integrity": "sha512-/Jqx6+N7A44n2BdvUr7pXhVr2vFjs6WGH3unZRczwrfiH0H1zY0QwKQMG/dtRiTlKGDKGukznPT8lx84/oEsZg==", "dev": true, "optional": true }, "@oxc-parser/binding-openharmony-arm64": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-openharmony-arm64/-/binding-openharmony-arm64-0.133.0.tgz", - "integrity": "sha512-51aByfXhPtLEdWG4a2Ihdw6cPWV1ei1AarALpFdDP8MLWDLE2NuUMgbo3DERR2Kt8fT/ok1GUvBiLxVGke9uUQ==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-openharmony-arm64/-/binding-openharmony-arm64-0.137.0.tgz", + "integrity": "sha512-9Uj0qHNNl+OgT1UTGwF7ixIXU6T1u2SbMidmgPy/h1h/fl2gRS6YpAxxY1gwHofcWjoTwkoMFd8xs5Vuj6GOFA==", "dev": true, "optional": true }, "@oxc-parser/binding-wasm32-wasi": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-wasm32-wasi/-/binding-wasm32-wasi-0.133.0.tgz", - "integrity": "sha512-2e16tkKp+wDO2GTAmXfxbBcCmGEaFPIJEIRBBmVKNVXSc8/fJsSIaBGyFTPHM9ST5GNWgJcYIt94rDTks+PLwA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-wasm32-wasi/-/binding-wasm32-wasi-0.137.0.tgz", + "integrity": "sha512-gW2vfkytNGgMVADiuzdvOfw0mWG9za20F/1fCJsif5aBMAvWJTSbpIXbIe0XkOe0VENk+PadpQ7cZgUy2sUJcA==", "dev": true, "optional": true, "requires": { - "@emnapi/core": "1.10.0", - "@emnapi/runtime": "1.10.0", - "@napi-rs/wasm-runtime": "^1.1.4" + "@emnapi/core": "1.11.1", + "@emnapi/runtime": "1.11.1", + "@napi-rs/wasm-runtime": "^1.1.5" } }, "@oxc-parser/binding-win32-arm64-msvc": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.133.0.tgz", - "integrity": "sha512-KPTNDKbxH1cglrqTyVeXHb4Pk4oksz8EcE1/v8zqU7N4UXbiHfA/IwtXZ2U77fnRAWBbgVkl/lZbL7o3hRdejg==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.137.0.tgz", + "integrity": "sha512-x+pFANF0yL5uK/6T7lu6SlR5qid6sp//eZXKLq5iNsIE+EQg6EaS8/wsW7E91nXXjpnPhSoMOHXShSVhGRdn8w==", "dev": true, "optional": true }, "@oxc-parser/binding-win32-ia32-msvc": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.133.0.tgz", - "integrity": "sha512-Una1bNYv9zCavQrfnDR9wuZVB3itLjCEH4Oz7i6CwAJN/Xq9b+zbbcxmvdkKvvJt4Ngc/MBmIYlbLo3zS4TQ0A==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.137.0.tgz", + "integrity": "sha512-sQUqym80PFi6McRsIqfJrSu2JrSClEZIXXD+/FjAFoULEKzOPsldIdFBG96xdX8aVMzCNQ9792FPx3MfkEIrFA==", "dev": true, "optional": true }, "@oxc-parser/binding-win32-x64-msvc": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.133.0.tgz", - "integrity": "sha512-kjBhCiOGSYTwDJQuuZa7a94JbP8htWu7J0X1KwH74kV2K5eYf6eyJRYmkpCDvr0XEL8tMxYI4WU1VekblFCLgg==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.137.0.tgz", + "integrity": "sha512-2AsevxlvNN4WKxpEn3RtqD5zbqMaXF+T7JXblsP4gVuY+vC9dXS4ED/PwfRCliFqoeisYS3Iro4DHzxr0TEvVA==", "dev": true, "optional": true }, "@oxc-project/types": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz", - "integrity": "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.137.0.tgz", + "integrity": "sha512-WT+Gb24i8hmvo85AIv2oEYouEXkRlKAlT9WaCa3TfLgNCN+GhrJOGZuIlMouAh38Qe4QOx26eUOVsq70qXrywA==", "dev": true }, "@oxc-resolver/binding-android-arm-eabi": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm-eabi/-/binding-android-arm-eabi-11.20.0.tgz", - "integrity": "sha512-IjfWOXRgJFNdORDl+Uf1aibNgZY2guOD3zmOhx1BGVb/MIiqlFTdmjpQNplSN58lhWehnX4UNqC3QwpUo8pjJg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm-eabi/-/binding-android-arm-eabi-11.21.3.tgz", + "integrity": "sha512-eNU11A2WNizh04v3uyaJCootrHIaS0B9aHYXvAvVnPNk4xYSjMUjHnhQ6dewPN2MRYDskV85d1N0Aw0WNWhcyg==", "dev": true, "optional": true }, "@oxc-resolver/binding-android-arm64": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm64/-/binding-android-arm64-11.20.0.tgz", - "integrity": "sha512-QqslZAuFQG8Q9xm7JuIn8JUbvywhSBMVhuQHtYW+auirZJloS41oxUUaBXk7uUhZJgp44c5zQLeVvmFaDQB+2Q==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm64/-/binding-android-arm64-11.21.3.tgz", + "integrity": "sha512-8Q+ZjTLvn2dIcWsrmhdrEihm7q+ag/k+mkry7Z+t0QbbHaVxXQfvH9AewyVMh/WrpEKhQ3DDgx9fYbqeCpeOEw==", "dev": true, "optional": true }, "@oxc-resolver/binding-darwin-arm64": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-arm64/-/binding-darwin-arm64-11.20.0.tgz", - "integrity": "sha512-MUcavykj2ewlR+kc5arpg4tC2RvzJkUxWtNv74pf7lcNk00GpIpN43vXMj+j6r4eMmfZhlb8hueKoIb8e9kAGQ==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-arm64/-/binding-darwin-arm64-11.21.3.tgz", + "integrity": "sha512-wkh0qKZGHXVUDxFw3oA1TXnU2BDYY/r775oJflGeIr8uDPPoN2pk8gijQIzYRT6hoql/lg3+Tx/SaTn9e2/aGg==", "dev": true, "optional": true }, "@oxc-resolver/binding-darwin-x64": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-x64/-/binding-darwin-x64-11.20.0.tgz", - "integrity": "sha512-BGB16nRUK5Etiv//ihPyzj8Lj1px0mhh4YIfe0FDf045ywknfSm0GEbiRESpr6Q4K82AvnyaRIhhluHByvS4bg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-x64/-/binding-darwin-x64-11.21.3.tgz", + "integrity": "sha512-HbNc23FAQYbuyDV2vBWMez4u4mrsm5RAkniGZAWqr6lYZ3N4beeqIb776jzwRl8qL2zRhHVXpUj97X0QgogVzg==", "dev": true, "optional": true }, "@oxc-resolver/binding-freebsd-x64": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-freebsd-x64/-/binding-freebsd-x64-11.20.0.tgz", - "integrity": "sha512-JZgtePaqj3qmD5XFHJaSLWzHRxQu0LaPkdoM1KJXYADvAaa83ijXHclV3ej3CueeW0wxfIAbGCZVP45J0CA7uQ==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-freebsd-x64/-/binding-freebsd-x64-11.21.3.tgz", + "integrity": "sha512-K6xNsTUPEUdfrn0+kbMq5nOUB5w1C5pavPQngt4TM2FpN91lP0PBe2srSpamb4d69O7h86oAi/qWX/kZNRSjkw==", "dev": true, "optional": true }, "@oxc-resolver/binding-linux-arm-gnueabihf": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-11.20.0.tgz", - "integrity": "sha512-hOQ/p3ry3v3SchUBXicrrnszaI/UmYzM4wtS4RGfwgVUX7a+HbyQSzJ5aOzu+o6XZkFkS3ZXN4PZAzhOb77OSg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-11.21.3.tgz", + "integrity": "sha512-VcFmOpcpWX1zoEy8M58tR2M9YxM+Z9RuQhqAx5q0CTmrruaP7Gveejg75hzd/5sg5nk9G3aLALEa3hE2FsmmTQ==", "dev": true, "optional": true }, "@oxc-resolver/binding-linux-arm-musleabihf": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-11.20.0.tgz", - "integrity": "sha512-2ArPksaw0AqeuGBfoS715VF+JvJQAhD2niWgjE5hVO+L+nAfikVQopvngCMX9x4BD8itWoQ3dnikrQyl5Ho5Jg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-11.21.3.tgz", + "integrity": "sha512-quVoxFLBy43hWaQbbDtQNRwAX5vX76mv7n64icAtQcJ3eNgVeblqmkupF/hAneNthdqSlnd1sTjb3aQSaDPaCQ==", "dev": true, "optional": true }, "@oxc-resolver/binding-linux-arm64-gnu": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-11.20.0.tgz", - "integrity": "sha512-0bJnmYFp62JdZ4nVMDUZ/C58BCZOCcqgKtnUlp7L9Ojf/czIN+3j72YlLPeWLkzlr6SlYvIQA4SGV/HyO0d+qg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-11.21.3.tgz", + "integrity": "sha512-X0AqNZgcD07Q4V3RDK18/vYOj/HQT/FnmEFGYS2jTWqY7JO13ryE3TEs3eAIgUJhBnNkpEaiXqz3VK8M7qQhWQ==", "dev": true, "optional": true }, "@oxc-resolver/binding-linux-arm64-musl": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-musl/-/binding-linux-arm64-musl-11.20.0.tgz", - "integrity": "sha512-wKHHzPKZo7Ufhv/Bt6yxT7FOgnIgW4gwXcJUipkShGp68W3wGVqvr1Sr0fY65lN0Oy6y41+g2kIDvkgZaMMUkw==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-musl/-/binding-linux-arm64-musl-11.21.3.tgz", + "integrity": "sha512-YkaQnaKYdbuaXvRt5Qd0GpbihzVnyfR6z1SpYfIUC6RTu4NF7lDKPjVkYb+jRI2gedVO2rVpN35Y6akG6ud4Lw==", "dev": true, "optional": true }, "@oxc-resolver/binding-linux-ppc64-gnu": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-11.20.0.tgz", - "integrity": "sha512-RN8goF7Ie0B79L4i4G6OeBocTgSC56vJbQ65VJje+oXnldVpLnOU7j/AQ/dP94TcCS+Yh6WG8u3Qt4ETteXFNQ==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-11.21.3.tgz", + "integrity": "sha512-gB9HwhrPiFqUzDeEq+y/CgAijz1YdI6BnXz5GaH2Pa9cWdutchlkGFAiAuGb/PjVQpiK6NFKzFuztxrweoit7A==", "dev": true, "optional": true }, "@oxc-resolver/binding-linux-riscv64-gnu": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-11.20.0.tgz", - "integrity": "sha512-5l1yU6/xQEqLZRzxqmMxJfWPslpwCmBsdDGaBvABPehxquCXDC7dd7oraNdKSJUMDXSM7VvVj8H2D2FTjU7oWw==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-11.21.3.tgz", + "integrity": "sha512-zjDWBlYk8QGv0H8dsPUWqkfjYIIjG2TvspGkzXL0eImbgxtZorA/klKeHyolevoT3Kvbi+1iMr9Lhrh7jf54Og==", "dev": true, "optional": true }, "@oxc-resolver/binding-linux-riscv64-musl": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-11.20.0.tgz", - "integrity": "sha512-xHEvkbgz6UC+A3JOyDQy76LkUaxsNSfIr3/GV8slwZsnuooJiIB34gzJfsyvR4JdCYNUUPsRJc/w/oWkODu+hg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-11.21.3.tgz", + "integrity": "sha512-4UfsQvacV388y1zpXL7C1x1FNYaV52JtuNRiuzrfQA2z1z6ElVrsidkGsrvQ5EgeSq1Pj7kaKqrgGkvFuxJ/tw==", "dev": true, "optional": true }, "@oxc-resolver/binding-linux-s390x-gnu": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-11.20.0.tgz", - "integrity": "sha512-aWPDUUmSeyHvlW+SoEUd+JIJsQhVhu6a5tBpDRMu058naPAchTgAVGCFy35zjbnFlt0i8hLWziff6HX0D3LU4g==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-11.21.3.tgz", + "integrity": "sha512-b5uH+HKH0MP5mNBYaK75SKsJbw52URqrx2LavYdq6wb0l3ExAG5niYRP9DWUNHdKilpaBVM2bXk9HNWrH3ew7Q==", "dev": true, "optional": true }, "@oxc-resolver/binding-linux-x64-gnu": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-gnu/-/binding-linux-x64-gnu-11.20.0.tgz", - "integrity": "sha512-x2YeSimvhJjKLVD8KSu8f/rqU1potcdEMkApIPJqjZWN7c2Fpt4g2X32WDg1p+XDAmyT7nuQGe0vnhvXeLbH+g==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-gnu/-/binding-linux-x64-gnu-11.21.3.tgz", + "integrity": "sha512-PjYlmilBpNRh2ntXNYAK3Am5w/nPfEpnU/96iNx7CI8EzAn12J4JRiec63wHJTH31nLoCNxBg/829pN+3CfG3Q==", "dev": true, "optional": true }, "@oxc-resolver/binding-linux-x64-musl": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-musl/-/binding-linux-x64-musl-11.20.0.tgz", - "integrity": "sha512-kcRLEIxpZefeYfLChjpgFf3ilBzRDZ+yobMrpRsQlSrxuFGtm3U6PMU7AaEpMqo3NfDGVyJJseAjnRLzMFHjwQ==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-musl/-/binding-linux-x64-musl-11.21.3.tgz", + "integrity": "sha512-QTBAb7JuHlZ7JUEyM8UiQi2f7m/L4swBhP2TNpYIDc9Wp/wRw1G/8sl6i13aIzQAXH7LKIm294LeOHd0lQR8zA==", "dev": true, "optional": true }, "@oxc-resolver/binding-openharmony-arm64": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-openharmony-arm64/-/binding-openharmony-arm64-11.20.0.tgz", - "integrity": "sha512-HHcfnApSZGtKhTiHqe8OZruOZe5XuFQH5/E0Yhj3u8fnFvzkM4/k6WjacUf4SvA0SPEAbfbgYmVPuo0VX/fIBQ==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-openharmony-arm64/-/binding-openharmony-arm64-11.21.3.tgz", + "integrity": "sha512-4j1DFwjwv36ec9kds0jU/ucQ5Ha4ERO/H95BxR5JFf0kqUUAJ1kwII7XhTc1vZrkdJkvLGC9Q2MbpObpum8RBg==", "dev": true, "optional": true }, "@oxc-resolver/binding-wasm32-wasi": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-wasm32-wasi/-/binding-wasm32-wasi-11.20.0.tgz", - "integrity": "sha512-Tn0y1XOFYHNfK1wp1Z5QK8Rcld/bsOwRISQXfqAZ5IBpv8Gz1IvV39fUWNprqNdRizgcvFhOzWwFun2zkJsyBg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-wasm32-wasi/-/binding-wasm32-wasi-11.21.3.tgz", + "integrity": "sha512-i8oluoel5kru/j1WNrjmQSiA3GQ7wvIYVR1IwIoZtKogAhya2iub+ZKIeSIkcJOrnzQ18Tzl/F+kL3fYOxZLvA==", "dev": true, "optional": true, "requires": { - "@emnapi/core": "1.10.0", - "@emnapi/runtime": "1.10.0", - "@napi-rs/wasm-runtime": "^1.1.4" + "@emnapi/core": "1.11.0", + "@emnapi/runtime": "1.11.0", + "@napi-rs/wasm-runtime": "^1.1.5" + }, + "dependencies": { + "@emnapi/core": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.0.tgz", + "integrity": "sha512-l9Oo58x0HOP5znGzVhYW9U3e5wVuA4LAZU2AGezTmkhO1CgQRFDhDg4nneHsu/t3WniXg9QrG2nIXL/ZS8ln8Q==", + "dev": true, + "optional": true, + "requires": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "@emnapi/runtime": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.0.tgz", + "integrity": "sha512-55coeOFKHv1ywEcUXJtWU5f+Jr/W5tZDvZig8DLKSwUN1JpROQ4rk/SNOQiFWmaR/VKF4zuFyW1B8JduOSv6Pg==", + "dev": true, + "optional": true, + "requires": { + "tslib": "^2.4.0" + } + } } }, "@oxc-resolver/binding-win32-arm64-msvc": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-11.20.0.tgz", - "integrity": "sha512-qPi25YNPe4YenS8MgsQU2+bIFHxxpLx1LVna2444cEHqNPhNjvWf9zqj4aWE43H9LpAsTmkkAlA3eL5ElBU3mA==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-11.21.3.tgz", + "integrity": "sha512-M/8dw8dD6aOs+NlPJax401CZB9I7Aut84isQLgALGGwke4Afvw+/7yYhZb94yXf6t2sPLhQLmSmtSV+2FhsOWg==", "dev": true, "optional": true }, "@oxc-resolver/binding-win32-x64-msvc": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-x64-msvc/-/binding-win32-x64-msvc-11.20.0.tgz", - "integrity": "sha512-Wb14jWEW8huH6It9F6sXd9vrYmIS7pMrgkU6sxpLxkP+9z+wRgs71hUEhRpcn8FOXAFa27FVWfY2tRpbfTzfLw==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-x64-msvc/-/binding-win32-x64-msvc-11.21.3.tgz", + "integrity": "sha512-H7BCt/VnS9hnmMp42eGhZ99izSCRvlnWwy/N71K1/J8QoExwY4262Z8QiEkMDtduRJrztayDxETTckmUuAVL9Q==", "dev": true, "optional": true }, @@ -2168,9 +2161,9 @@ "integrity": "sha512-cka47fVSo6lfQDIATYqb/vO1nvFfbPw7uWLayIXIhGETj0wcOOlrlkobOMDNQOFr9QOafegUPq13V2+6vtD7yg==" }, "@tybys/wasm-util": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", - "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", "dev": true, "optional": true, "requires": { @@ -2196,11 +2189,11 @@ "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==" }, "@types/node": { - "version": "24.12.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.2.tgz", - "integrity": "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g==", + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.3.tgz", + "integrity": "sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==", "requires": { - "undici-types": "~7.16.0" + "undici-types": "~7.18.0" } }, "@types/validator": { @@ -2428,22 +2421,22 @@ } }, "knip": { - "version": "6.16.1", - "resolved": "https://registry.npmjs.org/knip/-/knip-6.16.1.tgz", - "integrity": "sha512-TKMn1rxgH6h9vXR9Y0B+Cq7AdPTr9EI02IwoT65NzqYUkvoDQAaJ/aPybiFpAhZ1px6cNYYwXf86iHkBgzCo9w==", + "version": "6.27.0", + "resolved": "https://registry.npmjs.org/knip/-/knip-6.27.0.tgz", + "integrity": "sha512-CngYEYrD0n20N06FXA8n3u/0Wnnugoa+B9k14OP+iKIgkCHuzvIdsP3nfwjhByoc1WfogpxfiriMboAXFETDUw==", "dev": true, "requires": { "fdir": "^6.5.0", "formatly": "^0.3.0", "get-tsconfig": "4.14.0", "jiti": "^2.7.0", - "oxc-parser": "^0.133.0", - "oxc-resolver": "^11.20.0", + "oxc-parser": "^0.137.0", + "oxc-resolver": "11.21.3", "picomatch": "^4.0.4", "smol-toml": "^1.6.1", "strip-json-comments": "5.0.3", - "tinyglobby": "^0.2.16", - "unbash": "^3.0.0", + "tinyglobby": "^0.2.17", + "unbash": "^4.0.1", "yaml": "^2.9.0", "zod": "^4.1.11" }, @@ -2510,59 +2503,59 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "oxc-parser": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/oxc-parser/-/oxc-parser-0.133.0.tgz", - "integrity": "sha512-661RSx+ZcjBmjBYid+Fpp/2F5EbtildpeoZh5HdgnGs+jZ03nqQEQW8yGkt4BGyOC3OMPDQQRl8M5kqD2/g6jw==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/oxc-parser/-/oxc-parser-0.137.0.tgz", + "integrity": "sha512-yFImD+WLElJpLKy8llG1qe4DCmMsL18peRp8XP1JKfig/gISbJkglnpDtX2aTmAn10kZF7164HbN2H8QPsXxGg==", "dev": true, "requires": { - "@oxc-parser/binding-android-arm-eabi": "0.133.0", - "@oxc-parser/binding-android-arm64": "0.133.0", - "@oxc-parser/binding-darwin-arm64": "0.133.0", - "@oxc-parser/binding-darwin-x64": "0.133.0", - "@oxc-parser/binding-freebsd-x64": "0.133.0", - "@oxc-parser/binding-linux-arm-gnueabihf": "0.133.0", - "@oxc-parser/binding-linux-arm-musleabihf": "0.133.0", - "@oxc-parser/binding-linux-arm64-gnu": "0.133.0", - "@oxc-parser/binding-linux-arm64-musl": "0.133.0", - "@oxc-parser/binding-linux-ppc64-gnu": "0.133.0", - "@oxc-parser/binding-linux-riscv64-gnu": "0.133.0", - "@oxc-parser/binding-linux-riscv64-musl": "0.133.0", - "@oxc-parser/binding-linux-s390x-gnu": "0.133.0", - "@oxc-parser/binding-linux-x64-gnu": "0.133.0", - "@oxc-parser/binding-linux-x64-musl": "0.133.0", - "@oxc-parser/binding-openharmony-arm64": "0.133.0", - "@oxc-parser/binding-wasm32-wasi": "0.133.0", - "@oxc-parser/binding-win32-arm64-msvc": "0.133.0", - "@oxc-parser/binding-win32-ia32-msvc": "0.133.0", - "@oxc-parser/binding-win32-x64-msvc": "0.133.0", - "@oxc-project/types": "^0.133.0" + "@oxc-parser/binding-android-arm-eabi": "0.137.0", + "@oxc-parser/binding-android-arm64": "0.137.0", + "@oxc-parser/binding-darwin-arm64": "0.137.0", + "@oxc-parser/binding-darwin-x64": "0.137.0", + "@oxc-parser/binding-freebsd-x64": "0.137.0", + "@oxc-parser/binding-linux-arm-gnueabihf": "0.137.0", + "@oxc-parser/binding-linux-arm-musleabihf": "0.137.0", + "@oxc-parser/binding-linux-arm64-gnu": "0.137.0", + "@oxc-parser/binding-linux-arm64-musl": "0.137.0", + "@oxc-parser/binding-linux-ppc64-gnu": "0.137.0", + "@oxc-parser/binding-linux-riscv64-gnu": "0.137.0", + "@oxc-parser/binding-linux-riscv64-musl": "0.137.0", + "@oxc-parser/binding-linux-s390x-gnu": "0.137.0", + "@oxc-parser/binding-linux-x64-gnu": "0.137.0", + "@oxc-parser/binding-linux-x64-musl": "0.137.0", + "@oxc-parser/binding-openharmony-arm64": "0.137.0", + "@oxc-parser/binding-wasm32-wasi": "0.137.0", + "@oxc-parser/binding-win32-arm64-msvc": "0.137.0", + "@oxc-parser/binding-win32-ia32-msvc": "0.137.0", + "@oxc-parser/binding-win32-x64-msvc": "0.137.0", + "@oxc-project/types": "^0.137.0" } }, "oxc-resolver": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/oxc-resolver/-/oxc-resolver-11.20.0.tgz", - "integrity": "sha512-CblytBiV/a/ZXY34dsVU2NxhIOxMXst8CvDCtyBelVITgd7PLrKzbEbA6oKLdPjvDKDzCiW48qzmzZ+mYaqn+g==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/oxc-resolver/-/oxc-resolver-11.21.3.tgz", + "integrity": "sha512-2Mx3fKQz7+xgrBONjsxOgCGtMHOn38/HxMzW1I5efwXB5a4lRN0Vp40gYUJFBWJslcrvwoofTrqoTnLbwTd3pA==", "dev": true, "requires": { - "@oxc-resolver/binding-android-arm-eabi": "11.20.0", - "@oxc-resolver/binding-android-arm64": "11.20.0", - "@oxc-resolver/binding-darwin-arm64": "11.20.0", - "@oxc-resolver/binding-darwin-x64": "11.20.0", - "@oxc-resolver/binding-freebsd-x64": "11.20.0", - "@oxc-resolver/binding-linux-arm-gnueabihf": "11.20.0", - "@oxc-resolver/binding-linux-arm-musleabihf": "11.20.0", - "@oxc-resolver/binding-linux-arm64-gnu": "11.20.0", - "@oxc-resolver/binding-linux-arm64-musl": "11.20.0", - "@oxc-resolver/binding-linux-ppc64-gnu": "11.20.0", - "@oxc-resolver/binding-linux-riscv64-gnu": "11.20.0", - "@oxc-resolver/binding-linux-riscv64-musl": "11.20.0", - "@oxc-resolver/binding-linux-s390x-gnu": "11.20.0", - "@oxc-resolver/binding-linux-x64-gnu": "11.20.0", - "@oxc-resolver/binding-linux-x64-musl": "11.20.0", - "@oxc-resolver/binding-openharmony-arm64": "11.20.0", - "@oxc-resolver/binding-wasm32-wasi": "11.20.0", - "@oxc-resolver/binding-win32-arm64-msvc": "11.20.0", - "@oxc-resolver/binding-win32-x64-msvc": "11.20.0" + "@oxc-resolver/binding-android-arm-eabi": "11.21.3", + "@oxc-resolver/binding-android-arm64": "11.21.3", + "@oxc-resolver/binding-darwin-arm64": "11.21.3", + "@oxc-resolver/binding-darwin-x64": "11.21.3", + "@oxc-resolver/binding-freebsd-x64": "11.21.3", + "@oxc-resolver/binding-linux-arm-gnueabihf": "11.21.3", + "@oxc-resolver/binding-linux-arm-musleabihf": "11.21.3", + "@oxc-resolver/binding-linux-arm64-gnu": "11.21.3", + "@oxc-resolver/binding-linux-arm64-musl": "11.21.3", + "@oxc-resolver/binding-linux-ppc64-gnu": "11.21.3", + "@oxc-resolver/binding-linux-riscv64-gnu": "11.21.3", + "@oxc-resolver/binding-linux-riscv64-musl": "11.21.3", + "@oxc-resolver/binding-linux-s390x-gnu": "11.21.3", + "@oxc-resolver/binding-linux-x64-gnu": "11.21.3", + "@oxc-resolver/binding-linux-x64-musl": "11.21.3", + "@oxc-resolver/binding-openharmony-arm64": "11.21.3", + "@oxc-resolver/binding-wasm32-wasi": "11.21.3", + "@oxc-resolver/binding-win32-arm64-msvc": "11.21.3", + "@oxc-resolver/binding-win32-x64-msvc": "11.21.3" } }, "path-parse": { @@ -2749,15 +2742,15 @@ } }, "unbash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unbash/-/unbash-3.0.0.tgz", - "integrity": "sha512-FeFPZ/WFT0mbRCuydiZzpPFlrYN8ZUpphQKoq4EeElVIYjYyGzPMxQR/simUwCOJIyVhpFk4RbtyO7RuMpMnHA==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/unbash/-/unbash-4.0.4.tgz", + "integrity": "sha512-60m9IVGbavD6jholbxt0jVBXZkEB/HsMZq7Tyaghseve2/Sf0zQRAIfWsD34sde+DKP2tBxJS2wP88ZM0D1FhA==", "dev": true }, "undici-types": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", - "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==" + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==" }, "universalify": { "version": "0.1.2", From e7ddd4e830cf95e887cdbb697212507ef726acd7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Jul 2026 15:46:43 +0100 Subject: [PATCH 29/57] build(deps): bump the db-prod group across 1 directory with 6 updates (#884) Bumps the db-prod group with 6 updates in the /db directory: | Package | From | To | | --- | --- | --- | | [@clickhouse/client](https://github.com/ClickHouse/clickhouse-js) | `1.18.3` | `1.23.1` | | [@roostorg/db-migrator](https://github.com/roostorg/coop/tree/HEAD/migrator) | `1.1.0` | `1.1.1` | | [cassandra-driver](https://github.com/apache/cassandra-nodejs-driver) | `4.8.0` | `4.9.0` | | [kysely](https://github.com/kysely-org/kysely) | `0.28.17` | `0.29.4` | | [pg](https://github.com/brianc/node-postgres/tree/HEAD/packages/pg) | `8.20.0` | `8.22.0` | | [umzug](https://github.com/sequelize/umzug) | `3.8.2` | `3.8.3` | Updates `@clickhouse/client` from 1.18.3 to 1.23.1 - [Release notes](https://github.com/ClickHouse/clickhouse-js/releases) - [Changelog](https://github.com/ClickHouse/clickhouse-js/blob/main/CHANGELOG.md) - [Commits](https://github.com/ClickHouse/clickhouse-js/compare/1.18.3...client-1.23.1) Updates `@roostorg/db-migrator` from 1.1.0 to 1.1.1 - [Release notes](https://github.com/roostorg/coop/releases) - [Changelog](https://github.com/roostorg/coop/blob/main/CHANGELOG.md) - [Commits](https://github.com/roostorg/coop/commits/HEAD/migrator) Updates `cassandra-driver` from 4.8.0 to 4.9.0 - [Changelog](https://github.com/apache/cassandra-nodejs-driver/blob/trunk/CHANGELOG.md) - [Commits](https://github.com/apache/cassandra-nodejs-driver/compare/v4.8.0...v4.9.0) Updates `kysely` from 0.28.17 to 0.29.4 - [Release notes](https://github.com/kysely-org/kysely/releases) - [Commits](https://github.com/kysely-org/kysely/compare/v0.28.17...v0.29.4) Updates `pg` from 8.20.0 to 8.22.0 - [Changelog](https://github.com/brianc/node-postgres/blob/master/CHANGELOG.md) - [Commits](https://github.com/brianc/node-postgres/commits/pg@8.22.0/packages/pg) Updates `umzug` from 3.8.2 to 3.8.3 - [Release notes](https://github.com/sequelize/umzug/releases) - [Changelog](https://github.com/sequelize/umzug/blob/main/CHANGELOG.md) - [Commits](https://github.com/sequelize/umzug/compare/v3.8.2...v3.8.3) --- updated-dependencies: - dependency-name: "@clickhouse/client" dependency-version: 1.22.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: db-prod - dependency-name: "@roostorg/db-migrator" dependency-version: 1.1.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: db-prod - dependency-name: cassandra-driver dependency-version: 4.9.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: db-prod - dependency-name: kysely dependency-version: 0.29.2 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: db-prod - dependency-name: pg dependency-version: 8.22.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: db-prod - dependency-name: umzug dependency-version: 3.8.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: db-prod ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- db/package-lock.json | 562 ++++++++++--------------------------------- db/package.json | 12 +- 2 files changed, 138 insertions(+), 436 deletions(-) diff --git a/db/package-lock.json b/db/package-lock.json index b2f54e21..56aa171f 100644 --- a/db/package-lock.json +++ b/db/package-lock.json @@ -9,14 +9,14 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "@clickhouse/client": "^1.18.3", - "@roostorg/db-migrator": "^1.1.0", - "cassandra-driver": "^4.8.0", - "kysely": "^0.28.17", - "pg": "^8.7.1", + "@clickhouse/client": "^1.23.1", + "@roostorg/db-migrator": "^1.1.1", + "cassandra-driver": "^4.9.0", + "kysely": "^0.29.4", + "pg": "^8.22.0", "sequelize": "^6.37.8", "ts-node": "^10.9.2", - "umzug": "^3.0.0" + "umzug": "^3.8.3" }, "devDependencies": { "@types/pg": "^8.10.2", @@ -26,23 +26,14 @@ } }, "node_modules/@clickhouse/client": { - "version": "1.18.3", - "resolved": "https://registry.npmjs.org/@clickhouse/client/-/client-1.18.3.tgz", - "integrity": "sha512-340ngdYktL8PLUBK2QKSwe0o02tYfZSz1mSn1uXCEU8TxHvwh9pnQxElf9YHumDGj5gX/IdgxPsJTGMs82Hgug==", + "version": "1.23.1", + "resolved": "https://registry.npmjs.org/@clickhouse/client/-/client-1.23.1.tgz", + "integrity": "sha512-vs3/Zc1dHvT171btW5nMoPsPCJ6QVJ5pp7obxzO5sjqwFx/jjz9wwCAqcFOdc2DhprugDBaVn+4dVY8hG3A9nw==", "license": "Apache-2.0", - "dependencies": { - "@clickhouse/client-common": "1.18.3" - }, "engines": { - "node": ">=16" + "node": ">=20" } }, - "node_modules/@clickhouse/client-common": { - "version": "1.18.3", - "resolved": "https://registry.npmjs.org/@clickhouse/client-common/-/client-common-1.18.3.tgz", - "integrity": "sha512-3axzO3zvrsGT5PzDenxgWscltYCNRDbhaHWUgdsmcM9OnW/VnZn9EarOcZogr9P82Z0mQh+Jd2x+p2K4TFD2fA==", - "license": "Apache-2.0" - }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", @@ -133,41 +124,6 @@ "@emnapi/runtime": "^1.7.1" } }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/@oxc-parser/binding-android-arm-eabi": { "version": "0.137.0", "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm-eabi/-/binding-android-arm-eabi-0.137.0.tgz", @@ -863,13 +819,12 @@ ] }, "node_modules/@roostorg/db-migrator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@roostorg/db-migrator/-/db-migrator-1.1.0.tgz", - "integrity": "sha512-yRP2KsgU+nTX8p4lKUvygILnpK6xOeKYiP1ilpdluWVXPHQkSs/ZsnkyHGFPKuFn1+i2D+i5gMXUb1c1Jjtg2A==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@roostorg/db-migrator/-/db-migrator-1.1.1.tgz", + "integrity": "sha512-X7zlkiRVQkfk3IWQ2SXSrV8aGc27bf2mQrfsT/H50YQOE9oHCbTbkw14daehT/yGnnCFN0YPT+lI0X/a5FwrUQ==", "license": "ISC", "dependencies": { "@total-typescript/ts-reset": "^0.6.1", - "@types/yargs": "^17.0.24", "cassandra-driver": "^4.8.0", "sequelize": "^6.37.8", "umzug": "^3.0.0", @@ -877,19 +832,17 @@ } }, "node_modules/@rushstack/node-core-library": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-5.13.0.tgz", - "integrity": "sha512-IGVhy+JgUacAdCGXKUrRhwHMTzqhWwZUI+qEPcdzsb80heOw0QPbhhoVsoiMF7Klp8eYsp7hzpScMXmOa3Uhfg==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-4.0.2.tgz", + "integrity": "sha512-hyES82QVpkfQMeBMteQUnrhASL/KHPhd7iJ8euduwNJG4mu2GSOKybf0rOEjOm1Wz7CwJEUm9y0yD7jg2C1bfg==", "license": "MIT", "dependencies": { - "ajv": "~8.13.0", - "ajv-draft-04": "~1.0.0", - "ajv-formats": "~3.0.1", - "fs-extra": "~11.3.0", + "fs-extra": "~7.0.1", "import-lazy": "~4.0.0", "jju": "~1.4.0", "resolve": "~1.22.1", - "semver": "~7.5.4" + "semver": "~7.5.4", + "z-schema": "~5.0.2" }, "peerDependencies": { "@types/node": "*" @@ -916,12 +869,12 @@ } }, "node_modules/@rushstack/terminal": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/@rushstack/terminal/-/terminal-0.15.2.tgz", - "integrity": "sha512-7Hmc0ysK5077R/IkLS9hYu0QuNafm+TbZbtYVzCMbeOdMjaRboLKrhryjwZSRJGJzu+TV1ON7qZHeqf58XfLpA==", + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@rushstack/terminal/-/terminal-0.10.0.tgz", + "integrity": "sha512-UbELbXnUdc7EKwfH2sb8ChqNgapUOdqcCIdQP4NGxBpTZV2sQyeekuK3zmfQSa/MN+/7b4kBogl2wq0vpkpYGw==", "license": "MIT", "dependencies": { - "@rushstack/node-core-library": "5.13.0", + "@rushstack/node-core-library": "4.0.2", "supports-color": "~8.1.1" }, "peerDependencies": { @@ -934,12 +887,12 @@ } }, "node_modules/@rushstack/ts-command-line": { - "version": "4.23.7", - "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.23.7.tgz", - "integrity": "sha512-Gr9cB7DGe6uz5vq2wdr89WbVDKz0UeuFEn5H2CfWDe7JvjFFaiV15gi6mqDBTbHhHCWS7w8mF1h3BnIfUndqdA==", + "version": "4.19.1", + "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.19.1.tgz", + "integrity": "sha512-J7H768dgcpG60d7skZ5uSSwyCZs/S2HrWP1Ds8d1qYAyaaeJmpmmLr9BVw97RjFzmQPOYnoXcKA4GkqDCkduQg==", "license": "MIT", "dependencies": { - "@rushstack/terminal": "0.15.2", + "@rushstack/terminal": "0.10.0", "@types/argparse": "1.0.38", "argparse": "~1.0.9", "string-argv": "~0.3.1" @@ -1034,21 +987,6 @@ "integrity": "sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA==", "license": "MIT" }, - "node_modules/@types/yargs": { - "version": "17.0.35", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", - "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", - "license": "MIT" - }, "node_modules/acorn": { "version": "8.16.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", @@ -1082,53 +1020,6 @@ "node": ">=12.0" } }, - "node_modules/ajv": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", - "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-draft-04": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", - "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", - "license": "MIT", - "peerDependencies": { - "ajv": "^8.5.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/ajv-formats": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", - "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", - "license": "MIT", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, "node_modules/ansi-regex": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", @@ -1168,45 +1059,33 @@ "sprintf-js": "~1.0.2" } }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/cassandra-driver": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/cassandra-driver/-/cassandra-driver-4.8.0.tgz", - "integrity": "sha512-HritfMGq9V7SuESeSodHvArs0mLuMk7uh+7hQK2lqdvXrvm50aWxb4RPxkK3mPDdsgHjJ427xNRFITMH2ei+Sw==", + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/cassandra-driver/-/cassandra-driver-4.9.0.tgz", + "integrity": "sha512-svYpdkLIGjD0WmuuwkkeYbfBdPX1zksK2cDyT1mWjX53OVTzuWBOVy54K6PPij8GgYpIG+K82OryrBv/xNeuWg==", "license": "Apache-2.0", "dependencies": { - "@types/node": "^18.11.18", + "@types/node": "^20.14.8", "adm-zip": "~0.5.10", "long": "~5.2.3" }, "engines": { - "node": ">=18" + "node": ">=20" } }, "node_modules/cassandra-driver/node_modules/@types/node": { - "version": "18.19.130", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.130.tgz", - "integrity": "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==", + "version": "20.19.43", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz", + "integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==", "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~6.21.0" } }, "node_modules/cassandra-driver/node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "license": "MIT" }, "node_modules/cliui": { @@ -1223,6 +1102,16 @@ "node": ">=20" } }, + "node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": "^12.20.0 || >=14" + } + }, "node_modules/create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", @@ -1311,53 +1200,6 @@ "node": ">=6" } }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "license": "MIT" - }, - "node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-uri": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.4.tgz", - "integrity": "sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/fastq": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", - "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, "node_modules/fd-package-json": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fd-package-json/-/fd-package-json-2.0.0.tgz", @@ -1368,18 +1210,6 @@ "walk-up-path": "^4.0.0" } }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/formatly": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/formatly/-/formatly-0.3.0.tgz", @@ -1397,17 +1227,17 @@ } }, "node_modules/fs-extra": { - "version": "11.3.4", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.4.tgz", - "integrity": "sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", "license": "MIT", "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" }, "engines": { - "node": ">=14.14" + "node": ">=6 <7 || >=8" } }, "node_modules/function-bind": { @@ -1453,18 +1283,6 @@ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -1481,9 +1299,9 @@ } }, "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -1511,12 +1329,12 @@ "license": "MIT" }, "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", "license": "MIT", "dependencies": { - "hasown": "^2.0.2" + "hasown": "^2.0.3" }, "engines": { "node": ">= 0.4" @@ -1525,36 +1343,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, "node_modules/jiti": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", @@ -1571,20 +1359,11 @@ "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==", "license": "MIT" }, - "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "license": "MIT" - }, "node_modules/jsonfile": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", - "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, "optionalDependencies": { "graceful-fs": "^4.1.6" } @@ -1660,12 +1439,12 @@ } }, "node_modules/kysely": { - "version": "0.28.17", - "resolved": "https://registry.npmjs.org/kysely/-/kysely-0.28.17.tgz", - "integrity": "sha512-nbD8lB9EB3wNdMhOCdx5Li8DxnLbvKByylRLcJ1h+4SkrowVeECAyZlyiKMThF7xFdRz0jSQ2MoJr+wXux2y0Q==", + "version": "0.29.4", + "resolved": "https://registry.npmjs.org/kysely/-/kysely-0.29.4.tgz", + "integrity": "sha512-y5mVgQNkMbs1eK9Xyc0pmNdabN2wHhRYY/5r4W5HrUT1rYCEPeVNSj1RUJeSDKT3U0p+mXCvLgkrFuIafYI6BA==", "license": "MIT", "engines": { - "node": ">=20.0.0" + "node": ">=22.0.0" } }, "node_modules/lodash": { @@ -1674,6 +1453,20 @@ "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", "license": "MIT" }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", + "deprecated": "This package is deprecated. Use the optional chaining (?.) operator instead.", + "license": "MIT" + }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", + "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.", + "license": "MIT" + }, "node_modules/long": { "version": "5.2.5", "resolved": "https://registry.npmjs.org/long/-/long-5.2.5.tgz", @@ -1698,28 +1491,6 @@ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "license": "ISC" }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, "node_modules/moment": { "version": "2.30.1", "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", @@ -1823,14 +1594,14 @@ "license": "MIT" }, "node_modules/pg": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.20.0.tgz", - "integrity": "sha512-ldhMxz2r8fl/6QkXnBD3CR9/xg694oT6DZQ2s6c/RI28OjtSOpxnPrUCGOBJ46RCUxcWdx3p6kw/xnDHjKvaRA==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.22.0.tgz", + "integrity": "sha512-8wih1vVIBMxoUM2oB4soJsD9tDnDpLv4OXBJ+EJzFsvycD+lfyIreC2gGHq78f8jbLLt+bvlPTFdFZfJkOuzAA==", "license": "MIT", "dependencies": { - "pg-connection-string": "^2.12.0", - "pg-pool": "^3.13.0", - "pg-protocol": "^1.13.0", + "pg-connection-string": "^2.14.0", + "pg-pool": "^3.14.0", + "pg-protocol": "^1.15.0", "pg-types": "2.2.0", "pgpass": "1.0.5" }, @@ -1838,7 +1609,7 @@ "node": ">= 16.0.0" }, "optionalDependencies": { - "pg-cloudflare": "^1.3.0" + "pg-cloudflare": "^1.4.0" }, "peerDependencies": { "pg-native": ">=3.0.1" @@ -1850,16 +1621,16 @@ } }, "node_modules/pg-cloudflare": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.3.0.tgz", - "integrity": "sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.4.0.tgz", + "integrity": "sha512-Vo7z/6rrQYxpNRylp4Tlob2elzbh+N/MOQbxFVWCxS7oEx6jF53GTJFxK2WWpKuBRkmiin4Mt+xofFDjx09R0A==", "license": "MIT", "optional": true }, "node_modules/pg-connection-string": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.12.0.tgz", - "integrity": "sha512-U7qg+bpswf3Cs5xLzRqbXbQl85ng0mfSV/J0nnA31MCLgvEaAo7CIhmeyrmJpOr7o+zm0rXK+hNnT5l9RHkCkQ==", + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.14.0.tgz", + "integrity": "sha512-XwWDGcLRGCXAR8F/AM5bG7Q+A3Wm2s6QeEjlOKZLlH3UYcguiqCWKyWXVag5TLTIjR7oOJUY8kcADaZgWPyLeg==", "license": "MIT" }, "node_modules/pg-int8": { @@ -1872,18 +1643,18 @@ } }, "node_modules/pg-pool": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.13.0.tgz", - "integrity": "sha512-gB+R+Xud1gLFuRD/QgOIgGOBE2KCQPaPwkzBBGC9oG69pHTkhQeIuejVIk3/cnDyX39av2AxomQiyPT13WKHQA==", + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.14.0.tgz", + "integrity": "sha512-gKtPkFdQPU3DksooVLi9LsjZxrsBUZIpa+7aVx+LV5pNh0KzP4Zleud2po+ConrxbuXGBJ6Hfer6hdgpIBpBaw==", "license": "MIT", "peerDependencies": { "pg": ">=8.0" } }, "node_modules/pg-protocol": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.13.0.tgz", - "integrity": "sha512-zzdvXfS6v89r6v7OcFCHfHlyG/wvry1ALxZo4LqgUoy7W9xhBDMaqOuMiF3qEV45VqsN6rdlcehHrfDtlCPc8w==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.15.0.tgz", + "integrity": "sha512-cq9sECI5s0+uPUXjbz8ioyPJni6RzsRib0US67i5IoTZKw8fNeYlVE7u8F4dG7vEJJtc5wdD1K189lCCUwqWTQ==", "license": "MIT" }, "node_modules/pg-types": { @@ -1911,18 +1682,6 @@ "split2": "^4.1.0" } }, - "node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/pony-cause": { "version": "2.1.11", "resolved": "https://registry.npmjs.org/pony-cause/-/pony-cause-2.1.11.tgz", @@ -1971,35 +1730,6 @@ "node": ">=0.10.0" } }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/resolve": { "version": "1.22.12", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", @@ -2037,39 +1767,6 @@ "integrity": "sha512-hMD7odLOt3LkTjcif8aRZqi/hybjpLNgSk5oF5FCowfCjok6LukpN2bDX7R5wDmbgBQFn7YoBxSagmtXHaJYJw==", "license": "MIT" }, - "node_modules/reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, "node_modules/semver": { "version": "7.7.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", @@ -2266,7 +1963,6 @@ "version": "0.2.17", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", - "dev": true, "license": "MIT", "dependencies": { "fdir": "^6.5.0", @@ -2283,7 +1979,6 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, "license": "MIT", "engines": { "node": ">=12.0.0" @@ -2301,7 +1996,6 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -2310,18 +2004,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, "node_modules/toposort-class": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toposort-class/-/toposort-class-1.0.1.tgz", @@ -2405,15 +2087,15 @@ } }, "node_modules/umzug": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/umzug/-/umzug-3.8.2.tgz", - "integrity": "sha512-BEWEF8OJjTYVC56GjELeHl/1XjFejrD7aHzn+HldRJTx+pL1siBrKHZC8n4K/xL3bEzVA9o++qD1tK2CpZu4KA==", + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/umzug/-/umzug-3.8.3.tgz", + "integrity": "sha512-U9SRJI6LJvV0XwrqGMVPBkE26WHJklHZjtscJ2sEjUp7f+h4NH/25YGjPBernWLroVJvMnTkCAGC0bT0dd63qA==", "license": "MIT", "dependencies": { - "@rushstack/ts-command-line": "^4.12.2", + "@rushstack/ts-command-line": "4.19.1", "emittery": "^0.13.0", - "fast-glob": "^3.3.2", "pony-cause": "^2.1.4", + "tinyglobby": "^0.2.16", "type-fest": "^4.0.0" }, "engines": { @@ -2437,12 +2119,12 @@ "license": "MIT" }, "node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "license": "MIT", "engines": { - "node": ">= 10.0.0" + "node": ">= 4.0.0" } }, "node_modules/uuid": { @@ -2584,6 +2266,26 @@ "node": ">=6" } }, + "node_modules/z-schema": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.5.tgz", + "integrity": "sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==", + "license": "MIT", + "dependencies": { + "lodash.get": "^4.4.2", + "lodash.isequal": "^4.5.0", + "validator": "^13.7.0" + }, + "bin": { + "z-schema": "bin/z-schema" + }, + "engines": { + "node": ">=8.0.0" + }, + "optionalDependencies": { + "commander": "^9.4.1" + } + }, "node_modules/zod": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", diff --git a/db/package.json b/db/package.json index c5dec25b..39bcea0f 100644 --- a/db/package.json +++ b/db/package.json @@ -18,14 +18,14 @@ "author": "Roostorg", "license": "ISC", "dependencies": { - "@clickhouse/client": "^1.18.3", - "@roostorg/db-migrator": "^1.1.0", - "cassandra-driver": "^4.8.0", - "kysely": "^0.28.17", - "pg": "^8.7.1", + "@clickhouse/client": "^1.23.1", + "@roostorg/db-migrator": "^1.1.1", + "cassandra-driver": "^4.9.0", + "kysely": "^0.29.4", + "pg": "^8.22.0", "sequelize": "^6.37.8", "ts-node": "^10.9.2", - "umzug": "^3.0.0" + "umzug": "^3.8.3" }, "overrides": { "ajv": "~8.18.0", From b3e7c81cdf159f92063bfe3cc20148408ea8790c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Jul 2026 15:47:10 +0100 Subject: [PATCH 30/57] build(deps): bump nginx from 1.27-bookworm to 1.29.1-bookworm in /client (#775) Bumps nginx from 1.27-bookworm to 1.29.1-bookworm. --- updated-dependencies: - dependency-name: nginx dependency-version: 1.29.1-bookworm dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- client/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/Dockerfile b/client/Dockerfile index 6e442552..4cc76087 100644 --- a/client/Dockerfile +++ b/client/Dockerfile @@ -20,7 +20,7 @@ ARG VITE_OTEL_EXPORTER_OTLP_TRACES_ENDPOINT ENV DISABLE_ESLINT_PLUGIN=true RUN NODE_OPTIONS="--max-old-space-size=5250" VITE_OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=$VITE_OTEL_EXPORTER_OTLP_TRACES_ENDPOINT npm run build -FROM nginx:1.27-bookworm AS serve +FROM nginx:1.29.1-bookworm AS serve COPY --from=build /app/build /usr/share/nginx/html COPY nginx.conf /etc/nginx/conf.d/default.conf EXPOSE 80 From 0fbd6083212e4da935b20fc75fb63a3951d4fc72 Mon Sep 17 00:00:00 2001 From: serendipty01 <34604329+serendipty01@users.noreply.github.com> Date: Mon, 27 Jul 2026 20:19:16 +0530 Subject: [PATCH 31/57] build: bump Node to 24.18.0 for June 2026 security releases (#905) --- .nvmrc | 2 +- Dockerfile | 4 ++-- client/Dockerfile | 2 +- db/Dockerfile | 4 ++-- docker-compose.yaml | 4 ++-- docs/development/README.md | 6 +++--- nodejs-instrumentation/Dockerfile | 2 +- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.nvmrc b/.nvmrc index b832e400..ca5c3500 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -24.16.0 +24.18.0 diff --git a/Dockerfile b/Dockerfile index 9ee88eaa..0395f1e5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,7 +7,7 @@ # Docker's cache will let us skip installs when the dependencies haven't changed. # We build on debian because it has fewer dependency issues than Alpine for our # native modules, and we don't really care about the larger image size. -FROM node:24.14.1-bullseye-slim AS server_base +FROM node:24.18.0-bullseye-slim AS server_base WORKDIR /app RUN apt-get update && apt-get install -y --no-install-recommends git && rm -rf /var/lib/apt/lists/* @@ -19,7 +19,7 @@ FROM server_base AS build_backend RUN npm run build # make a shared layer that can be the base for worker and api images. -FROM node:24.14.1-bullseye-slim AS backend_base +FROM node:24.18.0-bullseye-slim AS backend_base WORKDIR /app RUN apt-get update && apt-get install dumb-init COPY --from=build_backend ["/app/package.json", "/app/package-lock.json", "./"] diff --git a/client/Dockerfile b/client/Dockerfile index 4cc76087..dcfff602 100644 --- a/client/Dockerfile +++ b/client/Dockerfile @@ -1,4 +1,4 @@ -FROM node:24.14.1-bullseye-slim AS client_base +FROM node:24.18.0-bullseye-slim AS client_base WORKDIR /app # ARG is used to get the release id into the ENV from the command line, and then diff --git a/db/Dockerfile b/db/Dockerfile index 15ffa9d5..d43d7ddc 100644 --- a/db/Dockerfile +++ b/db/Dockerfile @@ -1,4 +1,4 @@ -FROM node:24.14.1-bullseye-slim AS builder +FROM node:24.18.0-bullseye-slim AS builder WORKDIR /app @@ -13,7 +13,7 @@ RUN npm run build RUN npm prune --omit=dev -FROM node:24.14.1-bullseye-slim AS base +FROM node:24.18.0-bullseye-slim AS base WORKDIR /app diff --git a/docker-compose.yaml b/docker-compose.yaml index 88b542f9..5837d028 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -21,7 +21,7 @@ services: # Runs all migrations from scratch, after clearing the database(s). migrations: - image: node:24.14.1-bullseye-slim + image: node:24.18.0-bullseye-slim command: bash -c 'set -e npm i && ( [ "$CI" = "true" ] && npm run db:clean -- --env staging || true ) && for db in api-server-pg scylla clickhouse; do npm run db:create -- --db "$$db" --env staging; npm run db:update -- --db "$$db" --env staging; done' @@ -41,7 +41,7 @@ services: condition: service_healthy drop_dbs: - image: node:24.14.1-bullseye-slim + image: node:24.18.0-bullseye-slim command: bash -c 'npm i && npm run db:drop -- --env staging' working_dir: /src env_file: ./.env.githubci diff --git a/docs/development/README.md b/docs/development/README.md index 71a37e9f..2af6f6ca 100644 --- a/docs/development/README.md +++ b/docs/development/README.md @@ -27,9 +27,9 @@ To get Coop running: ``` 0.40.4 - Found '.nvmrc' with version <24.14.1> - v24.14.1 is already installed. - Now using node v24.14.1 (npm v11.11.0) + Found '.nvmrc' with version <24.18.0> + v24.18.0 is already installed. + Now using node v24.18.0 (npm v11.11.0) Docker version 29.4.3, build 055a478 ``` diff --git a/nodejs-instrumentation/Dockerfile b/nodejs-instrumentation/Dockerfile index 694c1458..efdac862 100644 --- a/nodejs-instrumentation/Dockerfile +++ b/nodejs-instrumentation/Dockerfile @@ -9,7 +9,7 @@ # - Grant the necessary access to `/autoinstrumentation` directory. `chmod -R go+r /autoinstrumentation` # - For auto-instrumentation by container injection, the Linux command cp is # used and must be availabe in the image. -FROM node:24.14.1 AS build +FROM node:24.18.0 AS build WORKDIR /operator-build From acc7b3a61fad77cb1cfbed85ee65d115e7fbda4d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Jul 2026 16:41:40 +0100 Subject: [PATCH 32/57] build(deps-dev): bump js-yaml from 4.2.0 to 4.3.0 in /server (#862) Bumps [js-yaml](https://github.com/nodeca/js-yaml) from 4.2.0 to 4.3.0. - [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md) - [Commits](https://github.com/nodeca/js-yaml/compare/4.2.0...4.3.0) --- updated-dependencies: - dependency-name: js-yaml dependency-version: 4.3.0 dependency-type: direct:development ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- server/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/package-lock.json b/server/package-lock.json index 4e3c10f6..5734199a 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -17776,9 +17776,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", - "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", + "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", "dev": true, "funding": [ { From 84a8485e1eba69acbffc4247b80e2a1092bb3b89 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Jul 2026 16:57:35 +0100 Subject: [PATCH 33/57] build(deps-dev): bump brace-expansion from 5.0.6 to 5.0.8 (#934) Bumps [brace-expansion](https://github.com/juliangruber/brace-expansion) from 5.0.6 to 5.0.8. - [Release notes](https://github.com/juliangruber/brace-expansion/releases) - [Commits](https://github.com/juliangruber/brace-expansion/compare/v5.0.6...v5.0.8) --- updated-dependencies: - dependency-name: brace-expansion dependency-version: 5.0.8 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2425e434..751b3328 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2567,16 +2567,16 @@ } }, "node_modules/brace-expansion": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", - "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz", + "integrity": "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, "engines": { - "node": "18 || 20 || >=22" + "node": "20 || >=22" } }, "node_modules/braces": { From 1694783c0008f02a59bf4db74029568739853a9d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Jul 2026 16:01:33 +0000 Subject: [PATCH 34/57] build(deps): bump ws from 8.20.0 to 8.21.1 in /client (#923) Bumps [ws](https://github.com/websockets/ws) from 8.20.0 to 8.21.1. - [Release notes](https://github.com/websockets/ws/releases) - [Commits](https://github.com/websockets/ws/compare/8.20.0...8.21.1) --- updated-dependencies: - dependency-name: ws dependency-version: 8.21.1 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- client/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index 03674dbb..4f09ab7a 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -15118,9 +15118,9 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "node_modules/ws": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz", - "integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==", + "version": "8.21.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.1.tgz", + "integrity": "sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw==", "dev": true, "license": "MIT", "engines": { From ee6d126f7e4dd916a607caf27f979e9e7daafce1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Jul 2026 17:38:22 +0100 Subject: [PATCH 35/57] build(deps-dev): bump js-yaml from 4.3.0 to 5.2.2 in /server (#946) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * build(deps-dev): bump js-yaml from 4.3.0 to 5.2.2 in /server Bumps [js-yaml](https://github.com/nodeca/js-yaml) from 4.3.0 to 5.2.2. - [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md) - [Commits](https://github.com/nodeca/js-yaml/compare/4.3.0...5.2.2) --- updated-dependencies: - dependency-name: js-yaml dependency-version: 5.2.2 dependency-type: direct:development ... Signed-off-by: dependabot[bot] * fix: use js-yaml named export Co-Authored-By: Codex --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Tao Bojlén Co-authored-by: Codex --- server/package-lock.json | 33 ++++++++++++++++--- server/package.json | 2 +- .../getRuleAlarmStatus.test.ts | 4 +-- .../getRuleAnomalyDetectionStatistics.ts | 4 +-- 4 files changed, 33 insertions(+), 10 deletions(-) diff --git a/server/package-lock.json b/server/package-lock.json index 5734199a..dcaf75a5 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -116,7 +116,7 @@ "jest": "^29.3.1", "jest-junit": "^16.0.0", "jest-light-runner": "^0.4.1", - "js-yaml": "^4.2.0", + "js-yaml": "^5.2.2", "jsonpath-plus": "^10.4.0", "knip": "^6.16.1", "supertest": "^6.2.2", @@ -2311,6 +2311,29 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", + "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -17776,9 +17799,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", - "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-5.2.2.tgz", + "integrity": "sha512-dayzUzKkJ1MkuUtZglSebU43utNXH0OWQByK9rKOOuYIO8M5TV1y+n8ALMdG0rdzBnfNkOmZEqrURepb0ejqBw==", "dev": true, "funding": [ { @@ -17795,7 +17818,7 @@ "argparse": "^2.0.1" }, "bin": { - "js-yaml": "bin/js-yaml.js" + "js-yaml": "bin/js-yaml.mjs" } }, "node_modules/jsdoc-type-pratt-parser": { diff --git a/server/package.json b/server/package.json index 4db8f40e..877428cc 100644 --- a/server/package.json +++ b/server/package.json @@ -135,7 +135,7 @@ "jest": "^29.3.1", "jest-junit": "^16.0.0", "jest-light-runner": "^0.4.1", - "js-yaml": "^4.2.0", + "js-yaml": "^5.2.2", "jsonpath-plus": "^10.4.0", "knip": "^6.16.1", "supertest": "^6.2.2", diff --git a/server/services/ruleAnomalyDetectionService/getRuleAlarmStatus.test.ts b/server/services/ruleAnomalyDetectionService/getRuleAlarmStatus.test.ts index c068a33f..0232efae 100644 --- a/server/services/ruleAnomalyDetectionService/getRuleAlarmStatus.test.ts +++ b/server/services/ruleAnomalyDetectionService/getRuleAlarmStatus.test.ts @@ -1,7 +1,7 @@ import { readFileSync } from 'fs'; import { dirname, join } from 'path'; import fc from 'fast-check'; -import yaml from 'js-yaml'; +import { load } from 'js-yaml'; import _ from 'lodash'; import { RuleAlarmStatus } from '../moderationConfigService/index.js'; @@ -17,7 +17,7 @@ const samplesPassRate = (samples: { passes: number; runs: number }[]) => sum(samples.map((it) => it.passes)) / sum(samples.map((it) => it.runs)); const __dirname = dirname(new URL(import.meta.url).pathname); -const tableDump = yaml.load( +const tableDump = load( // eslint-disable-next-line security/detect-non-literal-fs-filename readFileSync( join(__dirname, '../../test/stubs/rule_pass_sample_data.yaml'), diff --git a/server/test/stubs/getRuleAnomalyDetectionStatistics.ts b/server/test/stubs/getRuleAnomalyDetectionStatistics.ts index 1ebc36e1..f756e75d 100644 --- a/server/test/stubs/getRuleAnomalyDetectionStatistics.ts +++ b/server/test/stubs/getRuleAnomalyDetectionStatistics.ts @@ -1,11 +1,11 @@ import { readFileSync } from 'fs'; import { dirname, join } from 'path'; -import yaml from 'js-yaml'; +import { load } from 'js-yaml'; import { type GetRuleAnomalyDetectionStatistics } from '../../services/ruleAnomalyDetectionService/index.js'; const __dirname = dirname(new URL(import.meta.url).pathname); -const tableDump = yaml.load( +const tableDump = load( // eslint-disable-next-line security/detect-non-literal-fs-filename readFileSync(join(__dirname, './rule_pass_sample_data.yaml'), 'utf-8'), ) as { From bd54a2e1d65e23bab94b2a006f259183e6a24b8d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Jul 2026 17:58:10 +0100 Subject: [PATCH 36/57] build(deps): bump the server-prod-security group across 1 directory with 2 updates (#943) Bumps the server-prod-security group with 2 updates in the /server directory: [body-parser](https://github.com/expressjs/body-parser) and [fast-uri](https://github.com/fastify/fast-uri). Updates `body-parser` from 2.2.2 to 2.3.0 - [Release notes](https://github.com/expressjs/body-parser/releases) - [Changelog](https://github.com/expressjs/body-parser/blob/master/HISTORY.md) - [Commits](https://github.com/expressjs/body-parser/compare/v2.2.2...v2.3.0) Updates `fast-uri` from 3.1.2 to 3.1.4 - [Release notes](https://github.com/fastify/fast-uri/releases) - [Commits](https://github.com/fastify/fast-uri/compare/v3.1.2...v3.1.4) --- updated-dependencies: - dependency-name: body-parser dependency-version: 2.3.0 dependency-type: indirect dependency-group: server-prod-security - dependency-name: fast-uri dependency-version: 3.1.4 dependency-type: indirect dependency-group: server-prod-security ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- server/package-lock.json | 64 +++++++++++++++++++++++++++++----------- server/package.json | 2 +- 2 files changed, 48 insertions(+), 18 deletions(-) diff --git a/server/package-lock.json b/server/package-lock.json index dcaf75a5..e92403fa 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -13254,20 +13254,20 @@ } }, "node_modules/body-parser": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", - "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz", + "integrity": "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==", "license": "MIT", "dependencies": { "bytes": "^3.1.2", - "content-type": "^1.0.5", + "content-type": "^2.0.0", "debug": "^4.4.3", - "http-errors": "^2.0.0", - "iconv-lite": "^0.7.0", + "http-errors": "^2.0.1", + "iconv-lite": "^0.7.2", "on-finished": "^2.4.1", - "qs": "^6.14.1", - "raw-body": "^3.0.1", - "type-is": "^2.0.1" + "qs": "^6.15.2", + "raw-body": "^3.0.2", + "type-is": "^2.1.0" }, "engines": { "node": ">=18" @@ -13277,6 +13277,19 @@ "url": "https://opencollective.com/express" } }, + "node_modules/body-parser/node_modules/content-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", + "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/bowser": { "version": "2.14.1", "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.14.1.tgz", @@ -15292,9 +15305,9 @@ "dev": true }, "node_modules/fast-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", - "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.4.tgz", + "integrity": "sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw==", "funding": [ { "type": "github", @@ -21270,17 +21283,34 @@ } }, "node_modules/type-is": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", - "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", + "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==", "license": "MIT", "dependencies": { - "content-type": "^1.0.5", + "content-type": "^2.0.0", "media-typer": "^1.1.0", "mime-types": "^3.0.0" }, "engines": { - "node": ">= 0.6" + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/type-is/node_modules/content-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", + "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/type-is/node_modules/mime-db": { diff --git a/server/package.json b/server/package.json index 877428cc..6b8d2bce 100644 --- a/server/package.json +++ b/server/package.json @@ -163,7 +163,7 @@ "uuid": "^14.0.0", "fast-xml-parser": "^5.8.0", "axios": "^1.18.1", - "fast-uri": "^3.1.2", + "fast-uri": "^3.1.4", "protobufjs": "^7.6.3", "@protobufjs/utf8": "^1.1.1" } From 0951a1a2d7e0194a9f47584b301c8bca14c39b63 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Jul 2026 17:58:38 +0100 Subject: [PATCH 37/57] build(deps): bump the nodejs-instrumentation-prod group across 1 directory with 2 updates (#941) Bumps the nodejs-instrumentation-prod group with 2 updates in the /nodejs-instrumentation directory: [@opentelemetry/semantic-conventions](https://github.com/open-telemetry/opentelemetry-js) and [@opentelemetry/winston-transport](https://github.com/open-telemetry/opentelemetry-js-contrib/tree/HEAD/packages/winston-transport). Updates `@opentelemetry/semantic-conventions` from 1.41.1 to 1.43.0 - [Release notes](https://github.com/open-telemetry/opentelemetry-js/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-js/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-js/compare/semconv/v1.41.1...semconv/v1.43.0) Updates `@opentelemetry/winston-transport` from 0.29.0 to 0.30.0 - [Release notes](https://github.com/open-telemetry/opentelemetry-js-contrib/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-js-contrib/blob/main/packages/winston-transport/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-js-contrib/commits/host-metrics-v0.30.0/packages/winston-transport) --- updated-dependencies: - dependency-name: "@opentelemetry/semantic-conventions" dependency-version: 1.43.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: nodejs-instrumentation-prod - dependency-name: "@opentelemetry/winston-transport" dependency-version: 0.30.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: nodejs-instrumentation-prod ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- nodejs-instrumentation/package-lock.json | 25 ++++++++++++------------ nodejs-instrumentation/package.json | 4 ++-- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/nodejs-instrumentation/package-lock.json b/nodejs-instrumentation/package-lock.json index 3a9727c5..2006e434 100644 --- a/nodejs-instrumentation/package-lock.json +++ b/nodejs-instrumentation/package-lock.json @@ -20,8 +20,8 @@ "@opentelemetry/sdk-metrics": "^2.8.0", "@opentelemetry/sdk-node": "^0.221.0", "@opentelemetry/sdk-trace-base": "^2.8.0", - "@opentelemetry/semantic-conventions": "^1.41.1", - "@opentelemetry/winston-transport": "^0.29.0" + "@opentelemetry/semantic-conventions": "^1.43.0", + "@opentelemetry/winston-transport": "^0.30.0" }, "devDependencies": { "typescript": "^6.0.3" @@ -104,9 +104,9 @@ } }, "node_modules/@opentelemetry/api-logs": { - "version": "0.219.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.219.0.tgz", - "integrity": "sha512-FFx7YnaYJlIjqWW/AG/yAZ0L/NEY724PipXXXQLdtZPbLwBGbUMTGL1i/esI56TWfTUXxhLfpgrnWJCG8aUJyg==", + "version": "0.220.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.220.0.tgz", + "integrity": "sha512-CmVa4ImJ+ynfrPMNaAXHET6Bhb44SwzmfyVJFq9ni2jgXJR/l7C6gfVFddNmHP+ZOkP9cf4f9DBe68qVLTHc9w==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/api": "^1.3.0" @@ -1553,9 +1553,9 @@ } }, "node_modules/@opentelemetry/semantic-conventions": { - "version": "1.41.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.41.1.tgz", - "integrity": "sha512-/UhIkaZgPutTFmQ7RnIJGgDXZmtEJ7Dvi86xNTFWcnRxVRNk/aotsqDJYeEvDP+FSMB2SdW+pQzNMcWP0rwuNA==", + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.43.0.tgz", + "integrity": "sha512-eSYWTm620tTk45EKSedaUL8MFYI8hW164hIXsgIHyxu3VobUB3fFCu5t0hQby6OoWRPsG1KkKUG2M5UadiLiVg==", "license": "Apache-2.0", "engines": { "node": ">=14" @@ -1577,12 +1577,13 @@ } }, "node_modules/@opentelemetry/winston-transport": { - "version": "0.29.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/winston-transport/-/winston-transport-0.29.0.tgz", - "integrity": "sha512-/VX3xYkWZIFWLasxqPSj/3JFu8Ea8zP22zFsiiSlM8cHQ9ciqmbuX8k2N2CTTezBknteCMeOxt255s59RN3Wkg==", + "version": "0.30.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/winston-transport/-/winston-transport-0.30.0.tgz", + "integrity": "sha512-sBuqGxyOBIRmzVtzI7K67W2KZwmjHr8nVZPCi6A3uwei8hGlgnjqVWdgX9wIjcvGxg5qs3wkOeaLWuFz/MsOYA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api-logs": "^0.219.0", + "@opentelemetry/api-logs": "^0.220.0", + "@opentelemetry/semantic-conventions": "^1.41.1", "winston-transport": "4.*" }, "engines": { diff --git a/nodejs-instrumentation/package.json b/nodejs-instrumentation/package.json index 21b78a58..a53a74b8 100644 --- a/nodejs-instrumentation/package.json +++ b/nodejs-instrumentation/package.json @@ -21,8 +21,8 @@ "@opentelemetry/sdk-metrics": "^2.8.0", "@opentelemetry/sdk-node": "^0.221.0", "@opentelemetry/sdk-trace-base": "^2.8.0", - "@opentelemetry/semantic-conventions": "^1.41.1", - "@opentelemetry/winston-transport": "^0.29.0" + "@opentelemetry/semantic-conventions": "^1.43.0", + "@opentelemetry/winston-transport": "^0.30.0" }, "devDependencies": { "typescript": "^6.0.3" From 4986ef74cd0dd8563db4cb13b51b9b413cd38154 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Jul 2026 17:59:08 +0100 Subject: [PATCH 38/57] build(deps-dev): bump postcss from 8.5.12 to 8.5.23 in /client (#940) Bumps [postcss](https://github.com/postcss/postcss) from 8.5.12 to 8.5.23. - [Release notes](https://github.com/postcss/postcss/releases) - [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md) - [Commits](https://github.com/postcss/postcss/compare/8.5.12...8.5.23) --- updated-dependencies: - dependency-name: postcss dependency-version: 8.5.23 dependency-type: direct:development ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- client/package-lock.json | 16 ++++++++-------- client/package.json | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index 4f09ab7a..7e01b861 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -93,7 +93,7 @@ "eslint-plugin-react-hooks": "^7.0.1", "jsdom": "^29.1.1", "knip": "^6.16.1", - "postcss": "^8.5.10", + "postcss": "^8.5.23", "prop-types": "^15.8.1", "source-map-explorer": "^2.5.3", "storybook": "^9.1.20", @@ -10127,9 +10127,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "version": "3.3.16", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz", + "integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==", "funding": [ { "type": "github", @@ -10707,9 +10707,9 @@ } }, "node_modules/postcss": { - "version": "8.5.12", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.12.tgz", - "integrity": "sha512-W62t/Se6rA0Az3DfCL0AqJwXuKwBeYg6nOaIgzP+xZ7N5BFCI7DYi1qs6ygUYT6rvfi6t9k65UMLJC+PHZpDAA==", + "version": "8.5.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.23.tgz", + "integrity": "sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==", "funding": [ { "type": "opencollective", @@ -10726,7 +10726,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.11", + "nanoid": "^3.3.16", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, diff --git a/client/package.json b/client/package.json index 3a3f0043..5d213b73 100644 --- a/client/package.json +++ b/client/package.json @@ -101,7 +101,7 @@ "eslint-plugin-react-hooks": "^7.0.1", "jsdom": "^29.1.1", "knip": "^6.16.1", - "postcss": "^8.5.10", + "postcss": "^8.5.23", "prop-types": "^15.8.1", "source-map-explorer": "^2.5.3", "storybook": "^9.1.20", From 0ecffa354c66cf85e24062e6483142e520a0f26b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tao=20Bojl=C3=A9n?= Date: Tue, 28 Jul 2026 10:44:32 +0100 Subject: [PATCH 39/57] Replace dotenv with Node's built-in --env-file-if-exists (#929) * Replace dotenv with Node's built-in --env-file-if-exists Node 24 (this repo's runtime) has a built-in flag that loads a .env file when present and silently no-ops when absent - exactly the behavior dotenv/config provided. Remove the dotenv package from server and db, and switch all invocations to . Key details: - (not ) preserves the silent no-op when .env is absent, matching dotenv/config. CI/prod get env from the docker-compose directive, so .env is not present there. - is NOT allowed in NODE_OPTIONS, so the test scripts were restructured to invoke directly while keeping the ts-node loader in NODE_OPTIONS. - Removed from 4 test/e2e fixture files; env is now loaded by the node flag before any module reads process.env. - Removed dotenv from db/knip.json ignoreDependencies (no longer needed). - Updated db/src/index.ts shebang and db/README.md. uuid was NOT removed: the server uses v1 (timestamp) UUIDs in 23 files, and crypto.randomUUID() only generates v4 - there is no Node built-in replacement for v1. Co-Authored-By: pi * Remove redundant env-loading comments The comments restated the env-loading mechanism; removing them keeps the files clean. Behavior unchanged. Co-Authored-By: pi * Load env in test:e2e via --env-file-if-exists The e2e suite boots the IoC container (via the coop.ts fixture), which reads env vars like UI_URL at module load. Removing the `import 'dotenv/config'` from the fixture left nothing to load server/.env when playwright runs, so CI failed with "Missing env var UI_URL". Invoke playwright through `node --env-file-if-exists=.env` (the same pattern used for jest) so env loads before any test module imports the container. CI gets server/.env from the workflow's `cp server/.env.example server/.env` step; the -if-exists variant no-ops if it's absent. Co-Authored-By: pi --- db/README.md | 2 +- db/knip.json | 2 +- db/package-lock.json | 14 ----------- db/package.json | 1 - db/src/index.ts | 2 +- package.json | 10 ++++---- server/e2e/fixtures/coop.ts | 3 --- server/package-lock.json | 10 -------- server/package.json | 25 +++++++++---------- .../harness/transactionalPgPool.integ.test.ts | 2 -- .../test/integ/ncmec-submission.integ.test.ts | 2 -- server/test/integ/setupIntegrationServer.ts | 4 --- 12 files changed, 20 insertions(+), 57 deletions(-) diff --git a/db/README.md b/db/README.md index 48b0ca16..665fc46e 100644 --- a/db/README.md +++ b/db/README.md @@ -6,7 +6,7 @@ A Node.js CLI for running database migrations and seeds. ``` npm install -node --loader ts-node/esm --require dotenv/config index.ts +node --env-file-if-exists=.env --loader ts-node/esm index.ts ``` This displays available commands and arguments. diff --git a/db/knip.json b/db/knip.json index 55d171cf..134f5d39 100644 --- a/db/knip.json +++ b/db/knip.json @@ -2,5 +2,5 @@ "$schema": "./node_modules/knip/schema.json", "entry": ["src/index.ts"], "project": ["src/**/*.ts"], - "ignoreDependencies": ["ts-node", "dotenv"] + "ignoreDependencies": ["ts-node"] } diff --git a/db/package-lock.json b/db/package-lock.json index 56aa171f..1ce98f7e 100644 --- a/db/package-lock.json +++ b/db/package-lock.json @@ -20,7 +20,6 @@ }, "devDependencies": { "@types/pg": "^8.10.2", - "dotenv": "^17.4.2", "knip": "^6.26.0", "typescript": "^6.0.3" } @@ -1144,19 +1143,6 @@ "node": ">=0.3.1" } }, - "node_modules/dotenv": { - "version": "17.4.2", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.4.2.tgz", - "integrity": "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, "node_modules/dottie": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.7.tgz", diff --git a/db/package.json b/db/package.json index 39bcea0f..1528dcf9 100644 --- a/db/package.json +++ b/db/package.json @@ -34,7 +34,6 @@ }, "devDependencies": { "@types/pg": "^8.10.2", - "dotenv": "^17.4.2", "knip": "^6.26.0", "typescript": "^6.0.3" } diff --git a/db/src/index.ts b/db/src/index.ts index b240b12d..40c254b4 100755 --- a/db/src/index.ts +++ b/db/src/index.ts @@ -1,4 +1,4 @@ -#!/usr/bin/env -S node --loader ts-node/esm --require dotenv/config +#!/usr/bin/env -S node --env-file-if-exists=.env --loader ts-node/esm import { makeCli } from '@roostorg/db-migrator'; import apiServerPostgresConfig from './configs/api-server-pg.js'; diff --git a/package.json b/package.json index 2faf5192..37d43fb5 100644 --- a/package.json +++ b/package.json @@ -9,11 +9,11 @@ "client:start": "cd client && npm start", "server:start": "cd server && npm start", "create-org": "cd server && npm run create-org --", - "db:add": "cd db && npm i && NODE_OPTIONS=\"--loader ts-node/esm --require dotenv/config\" node src/index.ts add", - "db:clean": "cd db && npm i && NODE_OPTIONS=\"--loader ts-node/esm --require dotenv/config\" node src/index.ts clean", - "db:update": "cd db && npm i && NODE_OPTIONS=\"--loader ts-node/esm --require dotenv/config\" node src/index.ts apply", - "db:create": "cd db && npm i && NODE_OPTIONS=\"--loader ts-node/esm --require dotenv/config\" node src/index.ts create", - "db:drop": "cd db && npm i && NODE_OPTIONS=\"--loader ts-node/esm --require dotenv/config\" node src/index.ts drop", + "db:add": "cd db && npm i && NODE_OPTIONS=\"--loader ts-node/esm\" node --env-file-if-exists=.env src/index.ts add", + "db:clean": "cd db && npm i && NODE_OPTIONS=\"--loader ts-node/esm\" node --env-file-if-exists=.env src/index.ts clean", + "db:update": "cd db && npm i && NODE_OPTIONS=\"--loader ts-node/esm\" node --env-file-if-exists=.env src/index.ts apply", + "db:create": "cd db && npm i && NODE_OPTIONS=\"--loader ts-node/esm\" node --env-file-if-exists=.env src/index.ts create", + "db:drop": "cd db && npm i && NODE_OPTIONS=\"--loader ts-node/esm\" node --env-file-if-exists=.env src/index.ts drop", "check:prepush": "cd server && npm run check:prepush && cd ../client && npm run check:prepush", "typecheck": "cd server && npm run typecheck && cd ../client && npx tsc --noEmit", "prettier": "prettier --check \"./**/*.{ts,tsx,js,jsx,mjs,cjs,json,md,yaml,yml}\"", diff --git a/server/e2e/fixtures/coop.ts b/server/e2e/fixtures/coop.ts index 50a476ab..04500136 100644 --- a/server/e2e/fixtures/coop.ts +++ b/server/e2e/fixtures/coop.ts @@ -1,6 +1,3 @@ -// Load .env before anything reads process.env (the DI container does, heavily). -import 'dotenv/config'; - import { test as base, type APIRequestContext } from '@playwright/test'; import { uid } from 'uid'; diff --git a/server/package-lock.json b/server/package-lock.json index e92403fa..687ac8b2 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -103,7 +103,6 @@ "@typescript-eslint/eslint-plugin": "^8.57.2", "@typescript-eslint/parser": "^8.57.2", "copyfiles": "^2.4.1", - "dotenv": "^10.0.0", "eslint": "^9.39.4", "eslint-import-resolver-typescript": "^3.6.0", "eslint-plugin-functional": "^9.0.4", @@ -14117,15 +14116,6 @@ "node": ">=8" } }, - "node_modules/dotenv": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", - "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", diff --git a/server/package.json b/server/package.json index 6b8d2bce..90f1b97e 100644 --- a/server/package.json +++ b/server/package.json @@ -6,23 +6,23 @@ "scripts": { "build": "tsc && npm run copy-assets", "copy-assets": "copyfiles \"lib/**/*.lua\" transpiled/", - "start": "npm run copy-assets && tsc-watch --onSuccess \"node --trace-warnings --require dotenv/config ./transpiled/bin/www.js\"", - "start:trace": "npm run copy-assets && tsc-watch --onSuccess \"node --trace-warnings --require dotenv/config --require ../nodejs-instrumentation/transpiled/autoinstrumentation.js ./transpiled/bin/www.js\"", + "start": "npm run copy-assets && tsc-watch --onSuccess \"node --trace-warnings --env-file-if-exists=.env ./transpiled/bin/www.js\"", + "start:trace": "npm run copy-assets && tsc-watch --onSuccess \"node --trace-warnings --env-file-if-exists=.env --require ../nodejs-instrumentation/transpiled/autoinstrumentation.js ./transpiled/bin/www.js\"", "test": "npm run test:local", - "test:local": "NODE_OPTIONS=\"--no-warnings --loader ts-node/esm --require dotenv/config\" jest --watch --detectOpenHandles", - "test:prepush": "NODE_OPTIONS=\"--no-warnings --loader ts-node/esm --require dotenv/config\" jest --detectOpenHandles --no-cache --forceExit", - "test:ci": "NODE_OPTIONS=\"--loader ts-node/esm\" jest --ci --reporters=default --silent=false --reporters=jest-junit --no-cache --forceExit --runInBand", - "test:integ": "NODE_OPTIONS=\"--loader ts-node/esm\" jest --ci --reporters=default --silent=false --reporters=jest-junit --detectOpenHandles --no-cache --forceExit --runInBand --config jest.integ.config.cjs", + "test:local": "NODE_OPTIONS=\"--no-warnings --loader ts-node/esm\" node --env-file-if-exists=.env node_modules/.bin/jest --watch --detectOpenHandles", + "test:prepush": "NODE_OPTIONS=\"--no-warnings --loader ts-node/esm\" node --env-file-if-exists=.env node_modules/.bin/jest --detectOpenHandles --no-cache --forceExit", + "test:ci": "NODE_OPTIONS=\"--loader ts-node/esm\" node --env-file-if-exists=.env node_modules/.bin/jest --ci --reporters=default --silent=false --reporters=jest-junit --no-cache --forceExit --runInBand", + "test:integ": "NODE_OPTIONS=\"--loader ts-node/esm\" node --env-file-if-exists=.env node_modules/.bin/jest --ci --reporters=default --silent=false --reporters=jest-junit --detectOpenHandles --no-cache --forceExit --runInBand --config jest.integ.config.cjs", "typecheck": "tsc --noEmit", - "test:e2e": "playwright test --config e2e/playwright.config.ts", - "test:e2e:ui": "playwright test --config e2e/playwright.config.ts --ui", + "test:e2e": "node --env-file-if-exists=.env node_modules/.bin/playwright test --config e2e/playwright.config.ts", + "test:e2e:ui": "node --env-file-if-exists=.env node_modules/.bin/playwright test --config e2e/playwright.config.ts --ui", "e2e:install-browsers": "playwright install --with-deps chromium", "check:prepush": "npm run typecheck && npm run test:prepush", "lint": "eslint \"./**/*.{ts,tsx,js}\"", - "runWorkerOrJob": "node --loader ts-node/esm --require dotenv/config bin/run-worker-or-job.ts", - "create-org": "node --loader ts-node/esm --require dotenv/config bin/create-org-and-user.ts", - "get-invite": "node --loader ts-node/esm --require dotenv/config bin/get-invite-token.ts", - "recover-mrt-queue": "node --loader ts-node/esm --require dotenv/config bin/recover-mrt-queue.ts", + "runWorkerOrJob": "node --env-file-if-exists=.env --loader ts-node/esm bin/run-worker-or-job.ts", + "create-org": "node --env-file-if-exists=.env --loader ts-node/esm bin/create-org-and-user.ts", + "get-invite": "node --env-file-if-exists=.env --loader ts-node/esm bin/get-invite-token.ts", + "recover-mrt-queue": "node --env-file-if-exists=.env --loader ts-node/esm bin/recover-mrt-queue.ts", "knip": "knip" }, "author": "Roostorg", @@ -122,7 +122,6 @@ "@typescript-eslint/eslint-plugin": "^8.57.2", "@typescript-eslint/parser": "^8.57.2", "copyfiles": "^2.4.1", - "dotenv": "^10.0.0", "eslint": "^9.39.4", "eslint-import-resolver-typescript": "^3.6.0", "eslint-plugin-functional": "^9.0.4", diff --git a/server/test/harness/transactionalPgPool.integ.test.ts b/server/test/harness/transactionalPgPool.integ.test.ts index 9009b6b0..d8b299b7 100644 --- a/server/test/harness/transactionalPgPool.integ.test.ts +++ b/server/test/harness/transactionalPgPool.integ.test.ts @@ -4,8 +4,6 @@ * Proves that `createTransactionalTestDb` lets us wrap a whole test in a single * Postgres transaction that is rolled back at the end. */ -import 'dotenv/config'; - import { Kysely, PostgresDialect, sql } from 'kysely'; import pg from 'pg'; diff --git a/server/test/integ/ncmec-submission.integ.test.ts b/server/test/integ/ncmec-submission.integ.test.ts index 33af41a6..fc797948 100644 --- a/server/test/integ/ncmec-submission.integ.test.ts +++ b/server/test/integ/ncmec-submission.integ.test.ts @@ -1,5 +1,3 @@ -import 'dotenv/config'; - import { uid } from 'uid'; import { Headers } from 'undici'; diff --git a/server/test/integ/setupIntegrationServer.ts b/server/test/integ/setupIntegrationServer.ts index 79d0bca6..fcf88754 100644 --- a/server/test/integ/setupIntegrationServer.ts +++ b/server/test/integ/setupIntegrationServer.ts @@ -6,10 +6,6 @@ * Requires the docker-compose stack from `npm run up` and migrations applied * via `npm run db:update`. */ -// Load .env before any module that reads process.env (notably the IoC -// container). The unit-test `npm test` path goes through dotenv via its -// NODE_OPTIONS; `test:integ` does not, so we do it here. -import 'dotenv/config'; import * as superTest from 'supertest'; From 22d590e3f67386e98d076e44ae322263ae6fdc81 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Jul 2026 11:51:02 +0100 Subject: [PATCH 40/57] build(deps-dev): bump the client-dev group across 1 directory with 19 updates (#949) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * build(deps-dev): bump the client-dev group across 1 directory with 19 updates Bumps the client-dev group with 18 updates in the /client directory: | Package | From | To | | --- | --- | --- | | [@eslint/compat](https://github.com/eslint/rewrite/tree/HEAD/packages/compat) | `2.0.3` | `2.1.0` | | [@eslint/eslintrc](https://github.com/eslint/eslintrc) | `3.3.5` | `3.3.6` | | [@testing-library/jest-dom](https://github.com/testing-library/jest-dom) | `5.16.5` | `5.17.0` | | [@types/google.maps](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/google.maps) | `3.51.0` | `3.65.2` | | [@types/latlon-geohash](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/latlon-geohash) | `2.0.0` | `2.0.4` | | [@types/lodash](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/lodash) | `4.14.191` | `4.17.24` | | [@types/react-beautiful-dnd](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-beautiful-dnd) | `13.1.4` | `13.1.8` | | [@types/react-csv](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-csv) | `1.1.3` | `1.1.10` | | [@types/react-table](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-table) | `7.7.14` | `7.7.20` | | [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) | `8.57.2` | `8.64.0` | | [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/tree/HEAD/packages/plugin-react) | `5.1.4` | `5.2.0` | | [autoprefixer](https://github.com/postcss/autoprefixer) | `10.4.13` | `10.5.4` | | [eslint](https://github.com/eslint/eslint) | `9.39.4` | `9.39.5` | | [eslint-plugin-react-hooks](https://github.com/facebook/react/tree/HEAD/packages/eslint-plugin-react-hooks) | `7.0.1` | `7.1.1` | | [knip](https://github.com/webpro-nl/knip/tree/HEAD/packages/knip) | `6.16.1` | `6.27.0` | | [typescript](https://github.com/microsoft/TypeScript) | `5.3.2` | `5.9.3` | | [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) | `7.3.5` | `7.3.6` | | [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest) | `4.1.0` | `4.1.10` | Updates `@eslint/compat` from 2.0.3 to 2.1.0 - [Release notes](https://github.com/eslint/rewrite/releases) - [Changelog](https://github.com/eslint/rewrite/blob/main/packages/compat/CHANGELOG.md) - [Commits](https://github.com/eslint/rewrite/commits/compat-v2.1.0/packages/compat) Updates `@eslint/eslintrc` from 3.3.5 to 3.3.6 - [Release notes](https://github.com/eslint/eslintrc/releases) - [Changelog](https://github.com/eslint/eslintrc/blob/main/CHANGELOG.md) - [Commits](https://github.com/eslint/eslintrc/compare/eslintrc-v3.3.5...eslintrc-v3.3.6) Updates `@testing-library/jest-dom` from 5.16.5 to 5.17.0 - [Release notes](https://github.com/testing-library/jest-dom/releases) - [Changelog](https://github.com/testing-library/jest-dom/blob/main/CHANGELOG.md) - [Commits](https://github.com/testing-library/jest-dom/compare/v5.16.5...v5.17.0) Updates `@types/google.maps` from 3.51.0 to 3.65.2 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/google.maps) Updates `@types/latlon-geohash` from 2.0.0 to 2.0.4 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/latlon-geohash) Updates `@types/lodash` from 4.14.191 to 4.17.24 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/lodash) Updates `@types/react-beautiful-dnd` from 13.1.4 to 13.1.8 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react-beautiful-dnd) Updates `@types/react-csv` from 1.1.3 to 1.1.10 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react-csv) Updates `@types/react-table` from 7.7.14 to 7.7.20 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react-table) Updates `@typescript-eslint/eslint-plugin` from 8.57.2 to 8.64.0 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.64.0/packages/eslint-plugin) Updates `@typescript-eslint/parser` from 8.57.2 to 8.65.0 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.65.0/packages/parser) Updates `@vitejs/plugin-react` from 5.1.4 to 5.2.0 - [Release notes](https://github.com/vitejs/vite-plugin-react/releases) - [Changelog](https://github.com/vitejs/vite-plugin-react/blob/plugin-react@5.2.0/packages/plugin-react/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite-plugin-react/commits/plugin-react@5.2.0/packages/plugin-react) Updates `autoprefixer` from 10.4.13 to 10.5.4 - [Release notes](https://github.com/postcss/autoprefixer/releases) - [Changelog](https://github.com/postcss/autoprefixer/blob/main/CHANGELOG.md) - [Commits](https://github.com/postcss/autoprefixer/compare/10.4.13...10.5.4) Updates `eslint` from 9.39.4 to 9.39.5 - [Release notes](https://github.com/eslint/eslint/releases) - [Commits](https://github.com/eslint/eslint/compare/v9.39.4...v9.39.5) Updates `eslint-plugin-react-hooks` from 7.0.1 to 7.1.1 - [Release notes](https://github.com/facebook/react/releases) - [Changelog](https://github.com/react/react/blob/main/packages/eslint-plugin-react-hooks/CHANGELOG.md) - [Commits](https://github.com/facebook/react/commits/eslint-plugin-react-hooks@7.1.1/packages/eslint-plugin-react-hooks) Updates `knip` from 6.16.1 to 6.27.0 - [Release notes](https://github.com/webpro-nl/knip/releases) - [Commits](https://github.com/webpro-nl/knip/commits/knip@6.27.0/packages/knip) Updates `typescript` from 5.3.2 to 5.9.3 - [Release notes](https://github.com/microsoft/TypeScript/releases) - [Commits](https://github.com/microsoft/TypeScript/compare/v5.3.2...v5.9.3) Updates `vite` from 7.3.5 to 7.3.6 - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/v7.3.6/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/v7.3.6/packages/vite) Updates `vitest` from 4.1.0 to 4.1.10 - [Release notes](https://github.com/vitest-dev/vitest/releases) - [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md) - [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.10/packages/vitest) --- updated-dependencies: - dependency-name: "@eslint/compat" dependency-version: 2.1.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: client-dev - dependency-name: "@eslint/eslintrc" dependency-version: 3.3.6 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: client-dev - dependency-name: "@testing-library/jest-dom" dependency-version: 5.17.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: client-dev - dependency-name: "@types/google.maps" dependency-version: 3.65.2 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: client-dev - dependency-name: "@types/latlon-geohash" dependency-version: 2.0.4 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: client-dev - dependency-name: "@types/lodash" dependency-version: 4.17.24 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: client-dev - dependency-name: "@types/react-beautiful-dnd" dependency-version: 13.1.8 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: client-dev - dependency-name: "@types/react-csv" dependency-version: 1.1.10 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: client-dev - dependency-name: "@types/react-table" dependency-version: 7.7.20 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: client-dev - dependency-name: "@typescript-eslint/eslint-plugin" dependency-version: 8.64.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: client-dev - dependency-name: "@typescript-eslint/parser" dependency-version: 8.65.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: client-dev - dependency-name: "@vitejs/plugin-react" dependency-version: 5.2.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: client-dev - dependency-name: autoprefixer dependency-version: 10.5.4 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: client-dev - dependency-name: eslint dependency-version: 9.39.5 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: client-dev - dependency-name: eslint-plugin-react-hooks dependency-version: 7.1.1 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: client-dev - dependency-name: knip dependency-version: 6.27.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: client-dev - dependency-name: typescript dependency-version: 5.9.3 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: client-dev - dependency-name: vite dependency-version: 7.3.6 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: client-dev - dependency-name: vitest dependency-version: 4.1.10 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: client-dev ... Signed-off-by: dependabot[bot] * Fix client checks after dependency updates Co-Authored-By: Codex --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Tao Bojlén Co-authored-by: Codex --- client/package-lock.json | 1407 +++++++++-------- client/package.json | 36 +- client/src/utils/collections.ts | 6 +- .../investigation/ItemInvestigation.tsx | 4 +- .../dashboard/item_types/ItemTypePreview.tsx | 2 +- .../v2/ncmec/NCMECReviewUser.tsx | 2 +- .../dashboard/rules/rule_form/RuleForm.tsx | 2 +- .../rules/rule_form/RuleFormReducers.test.ts | 6 +- 8 files changed, 809 insertions(+), 656 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index 7e01b861..83caca8d 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -66,44 +66,44 @@ "web-vitals": "^1.0.1" }, "devDependencies": { - "@eslint/compat": "^2.0.3", - "@eslint/eslintrc": "^3.3.5", + "@eslint/compat": "^2.1.0", + "@eslint/eslintrc": "^3.3.6", "@faker-js/faker": "^8.4.1", "@storybook/react": "^9.1.20", "@storybook/react-vite": "^9.1.20", - "@testing-library/jest-dom": "^5.11.9", + "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^11.2.3", "@testing-library/user-event": "^12.6.0", - "@types/google.maps": "^3.48.7", - "@types/latlon-geohash": "^2.0.0", - "@types/lodash": "^4.14.181", + "@types/google.maps": "^3.65.2", + "@types/latlon-geohash": "^2.0.4", + "@types/lodash": "^4.17.24", "@types/react": "^18.0.0", - "@types/react-beautiful-dnd": "^13.1.4", - "@types/react-csv": "^1.1.3", + "@types/react-beautiful-dnd": "^13.1.8", + "@types/react-csv": "^1.1.10", "@types/react-dom": "^18.0.0", "@types/react-syntax-highlighter": "^13.5.2", - "@types/react-table": "^7.7.5", - "@typescript-eslint/eslint-plugin": "^8.57.2", + "@types/react-table": "^7.7.20", + "@typescript-eslint/eslint-plugin": "^8.64.0", "@typescript-eslint/parser": "^8.57.2", - "@vitejs/plugin-react": "^5.1.4", - "autoprefixer": "^10.4.7", - "eslint": "^9.39.4", + "@vitejs/plugin-react": "^5.2.0", + "autoprefixer": "^10.5.4", + "eslint": "^9.39.5", "eslint-plugin-custom-rules": "file:./eslint", "eslint-plugin-react": "^7.30.1", - "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-react-hooks": "^7.1.1", "jsdom": "^29.1.1", - "knip": "^6.16.1", + "knip": "^6.27.0", "postcss": "^8.5.23", "prop-types": "^15.8.1", "source-map-explorer": "^2.5.3", "storybook": "^9.1.20", "tailwindcss": "^3.4.19", - "typescript": "^5.3.2", - "vite": "^7.3.5", + "typescript": "^5.9.3", + "vite": "^7.3.6", "vite-plugin-commonjs": "^0.10.4", "vite-plugin-svgr": "^4.5.0", "vite-tsconfig-paths": "^6.1.1", - "vitest": "^4.1.0" + "vitest": "^4.1.10" } }, "eslint": { @@ -759,21 +759,21 @@ } }, "node_modules/@emnapi/core": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", - "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", + "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", "dev": true, "license": "MIT", "optional": true, "dependencies": { - "@emnapi/wasi-threads": "1.2.1", + "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" } }, "node_modules/@emnapi/runtime": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", - "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", + "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", "dev": true, "license": "MIT", "optional": true, @@ -782,9 +782,9 @@ } }, "node_modules/@emnapi/wasi-threads": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", - "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", + "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", "dev": true, "license": "MIT", "optional": true, @@ -1264,13 +1264,13 @@ } }, "node_modules/@eslint/compat": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@eslint/compat/-/compat-2.0.3.tgz", - "integrity": "sha512-SjIJhGigp8hmd1YGIBwh7Ovri7Kisl42GYFjrOyHhtfYGGoLW6teYi/5p8W50KSsawUPpuLOSmsq1bD0NGQLBw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@eslint/compat/-/compat-2.1.0.tgz", + "integrity": "sha512-LgaSCymEpw7tF53xvDw9SNsraPb1IBHxpdABIOM0hW8UAlP8znrjYtuxfR58FSJ3L9BhwD+FaPRFQpZq84Nh6g==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^1.1.1" + "@eslint/core": "^1.2.1" }, "engines": { "node": "^20.19.0 || ^22.13.0 || >=24" @@ -1326,9 +1326,9 @@ } }, "node_modules/@eslint/core": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.1.1.tgz", - "integrity": "sha512-QUPblTtE51/7/Zhfv8BDwO0qkkzQL7P/aWWbqcf4xWLEYn1oKjdO0gglQBB4GAsu7u6wjijbCmzsUTy6mnk6oQ==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", + "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1339,9 +1339,9 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", - "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.6.tgz", + "integrity": "sha512-l2Ul9PrHsPCKcEY/ac7VgFj9D80C7S68sOKc618SyHDPK36s1XcFebXY0iTzUVn4Yq+YbwvSnDmCz9yxjX+QrA==", "dev": true, "license": "MIT", "dependencies": { @@ -1351,7 +1351,7 @@ "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", - "js-yaml": "^4.1.1", + "js-yaml": "^4.3.0", "minimatch": "^3.1.5", "strip-json-comments": "^3.1.1" }, @@ -1363,9 +1363,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.39.4", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", - "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", + "version": "9.39.5", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.5.tgz", + "integrity": "sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A==", "dev": true, "license": "MIT", "engines": { @@ -1822,14 +1822,14 @@ } }, "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", - "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", + "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==", "dev": true, "license": "MIT", "optional": true, "dependencies": { - "@tybys/wasm-util": "^0.10.1" + "@tybys/wasm-util": "^0.10.3" }, "funding": { "type": "github", @@ -2202,9 +2202,9 @@ } }, "node_modules/@oxc-parser/binding-android-arm-eabi": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm-eabi/-/binding-android-arm-eabi-0.133.0.tgz", - "integrity": "sha512-l/44caGse+VpnY9gx0yvvc5QnnG3yG1FO3KZgYvNL1GZrfK86zIwAOgGEVlxDyRymzrU/KHiblPFpevKOmJmUA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm-eabi/-/binding-android-arm-eabi-0.137.0.tgz", + "integrity": "sha512-KDs+0VPdEmasOkpuJHW9V5WCF+cvYdMQv2Jd+aJXt+cxIx12NToRQRbXaRwUEDsZw+/jMk81Ve8ZFbjUkJTOwA==", "cpu": [ "arm" ], @@ -2219,9 +2219,9 @@ } }, "node_modules/@oxc-parser/binding-android-arm64": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm64/-/binding-android-arm64-0.133.0.tgz", - "integrity": "sha512-KUHmPMziLBp4u+zbrLdB7iWS7KshuZe+RAp7ELnY9SI9nNXBZ+dp8fiBqWOxhXqn+FQg3a4UcQhwmsJOKV8Jjg==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm64/-/binding-android-arm64-0.137.0.tgz", + "integrity": "sha512-WhALNzfy3x/RfC6bsqX+csavuUY0yHHE7XfgPE5M542uhoBZUUoGTPG+nkMbGoG4+gcfss5s7urMyn5QBHu0sw==", "cpu": [ "arm64" ], @@ -2236,9 +2236,9 @@ } }, "node_modules/@oxc-parser/binding-darwin-arm64": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.133.0.tgz", - "integrity": "sha512-q8dWmnU/8ea2tga9w2f1PinQ5rcMPDUGkF64T189b65YMjUomET4oy5oRldOr4AwOQkneOG/Zttnz1Dvrc62wg==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.137.0.tgz", + "integrity": "sha512-bFPr5hgmNMOMoyPTGtdsK4Ug21RovIPojRMgDDhSp1LtCnc/DkLwGONKjgRjszg677RlGnkYSviQ8hHaUPOVYA==", "cpu": [ "arm64" ], @@ -2253,9 +2253,9 @@ } }, "node_modules/@oxc-parser/binding-darwin-x64": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.133.0.tgz", - "integrity": "sha512-cOKeIELIB2bJnCKwqx4Rdj+1Lss/U6uCbLxRySZrhyOOQa1flKhwZFjEHRHxk8fU1NKmhK5OnTdPQ4CpjuFuVw==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.137.0.tgz", + "integrity": "sha512-CL5dMm1asqXIDZHg14FLxj3Mc36w8PI7xCWh1uA4is6z8g2XrIILoTcQYOxDbwzuk34RDPX5IAGUxZr6LA9KAg==", "cpu": [ "x64" ], @@ -2270,9 +2270,9 @@ } }, "node_modules/@oxc-parser/binding-freebsd-x64": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.133.0.tgz", - "integrity": "sha512-OpaSv4pW3KgFrMYQxTaS0aOE4T1DQF3qZE/4B6uqqv1KgPWWd4UQhJALi8PJPX1RRV5K7ThKXRfF7qGg2+3l1A==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.137.0.tgz", + "integrity": "sha512-79h8rYGnSlKPGWo7mHr2ixO6ea7aW8B0CT965SZ8SLbNnCOH5aOYBTeVXUY6eMvEaiLyWr8Skuiugr5pDYgLGw==", "cpu": [ "x64" ], @@ -2287,9 +2287,9 @@ } }, "node_modules/@oxc-parser/binding-linux-arm-gnueabihf": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.133.0.tgz", - "integrity": "sha512-JGK1wlGrGwxBIlVSF7KWTX1/ru6BEtf28fRROztDRkLfiW+Kxa4onnriezMIiogfn9hVw2KzYcKiLjkLR2ns8A==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.137.0.tgz", + "integrity": "sha512-ASgmlSimhGyr0lksgVIo6hibz1obnDq4qJbiMX/AzltfgPnanRrzG1Q+23g8ljOHOjv6dsznkUuCYL3gg0sY1Q==", "cpu": [ "arm" ], @@ -2304,9 +2304,9 @@ } }, "node_modules/@oxc-parser/binding-linux-arm-musleabihf": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.133.0.tgz", - "integrity": "sha512-yuZO533Ftonxn/iyoqQzURzLQHMspvsIyfiCSNi1t/ER4eIQaR0SsmUOUm5b/lmSig7IWIUa5/BrbEkAPwcilQ==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.137.0.tgz", + "integrity": "sha512-AU2J9aa22Sx32wRGnDjybOU9TQXXQUud5sdUi+ZB0XxwM8aToWLweV+yA0wlQm0yIUVqljquqoHCYEq9II8gJQ==", "cpu": [ "arm" ], @@ -2321,13 +2321,16 @@ } }, "node_modules/@oxc-parser/binding-linux-arm64-gnu": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.133.0.tgz", - "integrity": "sha512-hvpbqT5pN2rR+3+xtWeizwfR/aZ0vGceg6TqYMl+ToxMpk9/tmnX7kSvQnfEUkoua8mhogzvIKsAkn0wxgblBA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.137.0.tgz", + "integrity": "sha512-GdEtiG89yMr7XkUGxifgodXEEm2f+xW2f9CpDjlgAnBOwhTmrpQMvhOGobLVKUyzf/qHBXW16smk5zbF3nZU6w==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -2338,13 +2341,16 @@ } }, "node_modules/@oxc-parser/binding-linux-arm64-musl": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.133.0.tgz", - "integrity": "sha512-wJQGamIosQBoJHW9+S5XxrtKRo3eyJxsnS1XCPrqN0LHi8uw1pTqqTfn3t/NVuvbBg7Pumn4ez9Eidgcn0xbEg==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.137.0.tgz", + "integrity": "sha512-EGJ+Bs8iXx8KBH8DQ5BLoEm5lnHaYjlh4/8j8vFhrr/6z4tqONy5BZDzLpKmmNWlN6Hlc5r8YOuBVHqZ9vRFEQ==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -2355,13 +2361,16 @@ } }, "node_modules/@oxc-parser/binding-linux-ppc64-gnu": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.133.0.tgz", - "integrity": "sha512-Koaz32/O5+abIfrNGdyndgRvdOZ9jEf5/z3Ep9h3h2QWpdDiUQpVwgH0OcMXCs+l9aXxPLtkupqyVig9W6FDKw==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.137.0.tgz", + "integrity": "sha512-vzFUQENy/fnbSe5DZWovq6tIBc1uhuMztanSW6rz1e9WdQE4gHwYuD7ZII6JnrJifd1R3RSoqiZbgRFlVL2tYQ==", "cpu": [ "ppc64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -2372,13 +2381,16 @@ } }, "node_modules/@oxc-parser/binding-linux-riscv64-gnu": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.133.0.tgz", - "integrity": "sha512-R4vOjWzxhnNWHnVLeiB6jNuIifdy9vcMXZGPc7StXcxBovI+U2zg1QhZ9o8OjV80oGivs1lX5NfPLzk4IPqlRA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.137.0.tgz", + "integrity": "sha512-SfVI14HBQs9gtLcUD5hTt5hsNbdrqSUNg9S8muN+LhVQ5nf1WwH3hAoK6B9NKgdYgWAQSXFXGiiBedQ4r/BKuw==", "cpu": [ "riscv64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -2389,13 +2401,16 @@ } }, "node_modules/@oxc-parser/binding-linux-riscv64-musl": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.133.0.tgz", - "integrity": "sha512-iwgBNUTHiMdxARLYuM0SBlnYeb19iw1Ea5M+4ERZupCsBMLArti6FyZ6UfFjJxIiTDr2oW2DGQFxlQVQ/dW9rA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.137.0.tgz", + "integrity": "sha512-e7Ppy4FCIFNQxT/ikSeIWFoQ0l+N9vgtRBtLcyZXeolTzApyVoPqEXsYPrcdM/9i0Bwk8knvYd37vaEMxHyi6g==", "cpu": [ "riscv64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -2406,13 +2421,16 @@ } }, "node_modules/@oxc-parser/binding-linux-s390x-gnu": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.133.0.tgz", - "integrity": "sha512-ZwZNo8FZmB/gVfboQl+wXilBigGl+6nQQs+nITOeAP/HcAOjiHl6XZJL9F/KXNEspODQcbjAiyjUbeCJd9a0fA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.137.0.tgz", + "integrity": "sha512-Bho5qFwdhqsIFR7gipYEUlqvi3SRrY8sugxXig380MIaakBB1PyU9+7dBiBVScfImTNWhijUxdBwqrprGdq5WA==", "cpu": [ "s390x" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -2423,13 +2441,16 @@ } }, "node_modules/@oxc-parser/binding-linux-x64-gnu": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.133.0.tgz", - "integrity": "sha512-govCvWx1dBlED3uu4qXctxpRcouu9I8Kn+DBktGCl760JtlGJzc9l/OmPJKlYWSbrRqKkMZehNeZ/4Wfma7uSA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.137.0.tgz", + "integrity": "sha512-36mGWtg7PyFzjJwGDkH6/F4o2nIDEoKXLPr/X/lwqklkomQwJJt1I5GJVmGhovUEmgPK5WAeAZMqlFCehwiy9Q==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -2440,13 +2461,16 @@ } }, "node_modules/@oxc-parser/binding-linux-x64-musl": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.133.0.tgz", - "integrity": "sha512-ssTlpXD5Mq9uCssDJPzlRWqBt4Y7Zzd9i+XZhWmK/9Y6KUIuAxVYTYiI8lxcGWi0+3/Cz4A8q9UrD4NK9Y2j7g==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.137.0.tgz", + "integrity": "sha512-/Jqx6+N7A44n2BdvUr7pXhVr2vFjs6WGH3unZRczwrfiH0H1zY0QwKQMG/dtRiTlKGDKGukznPT8lx84/oEsZg==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -2457,9 +2481,9 @@ } }, "node_modules/@oxc-parser/binding-openharmony-arm64": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-openharmony-arm64/-/binding-openharmony-arm64-0.133.0.tgz", - "integrity": "sha512-51aByfXhPtLEdWG4a2Ihdw6cPWV1ei1AarALpFdDP8MLWDLE2NuUMgbo3DERR2Kt8fT/ok1GUvBiLxVGke9uUQ==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-openharmony-arm64/-/binding-openharmony-arm64-0.137.0.tgz", + "integrity": "sha512-9Uj0qHNNl+OgT1UTGwF7ixIXU6T1u2SbMidmgPy/h1h/fl2gRS6YpAxxY1gwHofcWjoTwkoMFd8xs5Vuj6GOFA==", "cpu": [ "arm64" ], @@ -2474,9 +2498,9 @@ } }, "node_modules/@oxc-parser/binding-wasm32-wasi": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-wasm32-wasi/-/binding-wasm32-wasi-0.133.0.tgz", - "integrity": "sha512-2e16tkKp+wDO2GTAmXfxbBcCmGEaFPIJEIRBBmVKNVXSc8/fJsSIaBGyFTPHM9ST5GNWgJcYIt94rDTks+PLwA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-wasm32-wasi/-/binding-wasm32-wasi-0.137.0.tgz", + "integrity": "sha512-gW2vfkytNGgMVADiuzdvOfw0mWG9za20F/1fCJsif5aBMAvWJTSbpIXbIe0XkOe0VENk+PadpQ7cZgUy2sUJcA==", "cpu": [ "wasm32" ], @@ -2484,18 +2508,18 @@ "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "1.10.0", - "@emnapi/runtime": "1.10.0", - "@napi-rs/wasm-runtime": "^1.1.4" + "@emnapi/core": "1.11.1", + "@emnapi/runtime": "1.11.1", + "@napi-rs/wasm-runtime": "^1.1.5" }, "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@oxc-parser/binding-win32-arm64-msvc": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.133.0.tgz", - "integrity": "sha512-KPTNDKbxH1cglrqTyVeXHb4Pk4oksz8EcE1/v8zqU7N4UXbiHfA/IwtXZ2U77fnRAWBbgVkl/lZbL7o3hRdejg==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.137.0.tgz", + "integrity": "sha512-x+pFANF0yL5uK/6T7lu6SlR5qid6sp//eZXKLq5iNsIE+EQg6EaS8/wsW7E91nXXjpnPhSoMOHXShSVhGRdn8w==", "cpu": [ "arm64" ], @@ -2510,9 +2534,9 @@ } }, "node_modules/@oxc-parser/binding-win32-ia32-msvc": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.133.0.tgz", - "integrity": "sha512-Una1bNYv9zCavQrfnDR9wuZVB3itLjCEH4Oz7i6CwAJN/Xq9b+zbbcxmvdkKvvJt4Ngc/MBmIYlbLo3zS4TQ0A==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.137.0.tgz", + "integrity": "sha512-sQUqym80PFi6McRsIqfJrSu2JrSClEZIXXD+/FjAFoULEKzOPsldIdFBG96xdX8aVMzCNQ9792FPx3MfkEIrFA==", "cpu": [ "ia32" ], @@ -2527,9 +2551,9 @@ } }, "node_modules/@oxc-parser/binding-win32-x64-msvc": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.133.0.tgz", - "integrity": "sha512-kjBhCiOGSYTwDJQuuZa7a94JbP8htWu7J0X1KwH74kV2K5eYf6eyJRYmkpCDvr0XEL8tMxYI4WU1VekblFCLgg==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.137.0.tgz", + "integrity": "sha512-2AsevxlvNN4WKxpEn3RtqD5zbqMaXF+T7JXblsP4gVuY+vC9dXS4ED/PwfRCliFqoeisYS3Iro4DHzxr0TEvVA==", "cpu": [ "x64" ], @@ -2544,9 +2568,9 @@ } }, "node_modules/@oxc-project/types": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz", - "integrity": "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.137.0.tgz", + "integrity": "sha512-WT+Gb24i8hmvo85AIv2oEYouEXkRlKAlT9WaCa3TfLgNCN+GhrJOGZuIlMouAh38Qe4QOx26eUOVsq70qXrywA==", "dev": true, "license": "MIT", "funding": { @@ -2554,9 +2578,9 @@ } }, "node_modules/@oxc-resolver/binding-android-arm-eabi": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm-eabi/-/binding-android-arm-eabi-11.20.0.tgz", - "integrity": "sha512-IjfWOXRgJFNdORDl+Uf1aibNgZY2guOD3zmOhx1BGVb/MIiqlFTdmjpQNplSN58lhWehnX4UNqC3QwpUo8pjJg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm-eabi/-/binding-android-arm-eabi-11.21.3.tgz", + "integrity": "sha512-eNU11A2WNizh04v3uyaJCootrHIaS0B9aHYXvAvVnPNk4xYSjMUjHnhQ6dewPN2MRYDskV85d1N0Aw0WNWhcyg==", "cpu": [ "arm" ], @@ -2568,9 +2592,9 @@ ] }, "node_modules/@oxc-resolver/binding-android-arm64": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm64/-/binding-android-arm64-11.20.0.tgz", - "integrity": "sha512-QqslZAuFQG8Q9xm7JuIn8JUbvywhSBMVhuQHtYW+auirZJloS41oxUUaBXk7uUhZJgp44c5zQLeVvmFaDQB+2Q==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm64/-/binding-android-arm64-11.21.3.tgz", + "integrity": "sha512-8Q+ZjTLvn2dIcWsrmhdrEihm7q+ag/k+mkry7Z+t0QbbHaVxXQfvH9AewyVMh/WrpEKhQ3DDgx9fYbqeCpeOEw==", "cpu": [ "arm64" ], @@ -2582,9 +2606,9 @@ ] }, "node_modules/@oxc-resolver/binding-darwin-arm64": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-arm64/-/binding-darwin-arm64-11.20.0.tgz", - "integrity": "sha512-MUcavykj2ewlR+kc5arpg4tC2RvzJkUxWtNv74pf7lcNk00GpIpN43vXMj+j6r4eMmfZhlb8hueKoIb8e9kAGQ==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-arm64/-/binding-darwin-arm64-11.21.3.tgz", + "integrity": "sha512-wkh0qKZGHXVUDxFw3oA1TXnU2BDYY/r775oJflGeIr8uDPPoN2pk8gijQIzYRT6hoql/lg3+Tx/SaTn9e2/aGg==", "cpu": [ "arm64" ], @@ -2596,9 +2620,9 @@ ] }, "node_modules/@oxc-resolver/binding-darwin-x64": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-x64/-/binding-darwin-x64-11.20.0.tgz", - "integrity": "sha512-BGB16nRUK5Etiv//ihPyzj8Lj1px0mhh4YIfe0FDf045ywknfSm0GEbiRESpr6Q4K82AvnyaRIhhluHByvS4bg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-x64/-/binding-darwin-x64-11.21.3.tgz", + "integrity": "sha512-HbNc23FAQYbuyDV2vBWMez4u4mrsm5RAkniGZAWqr6lYZ3N4beeqIb776jzwRl8qL2zRhHVXpUj97X0QgogVzg==", "cpu": [ "x64" ], @@ -2610,9 +2634,9 @@ ] }, "node_modules/@oxc-resolver/binding-freebsd-x64": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-freebsd-x64/-/binding-freebsd-x64-11.20.0.tgz", - "integrity": "sha512-JZgtePaqj3qmD5XFHJaSLWzHRxQu0LaPkdoM1KJXYADvAaa83ijXHclV3ej3CueeW0wxfIAbGCZVP45J0CA7uQ==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-freebsd-x64/-/binding-freebsd-x64-11.21.3.tgz", + "integrity": "sha512-K6xNsTUPEUdfrn0+kbMq5nOUB5w1C5pavPQngt4TM2FpN91lP0PBe2srSpamb4d69O7h86oAi/qWX/kZNRSjkw==", "cpu": [ "x64" ], @@ -2624,9 +2648,9 @@ ] }, "node_modules/@oxc-resolver/binding-linux-arm-gnueabihf": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-11.20.0.tgz", - "integrity": "sha512-hOQ/p3ry3v3SchUBXicrrnszaI/UmYzM4wtS4RGfwgVUX7a+HbyQSzJ5aOzu+o6XZkFkS3ZXN4PZAzhOb77OSg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-11.21.3.tgz", + "integrity": "sha512-VcFmOpcpWX1zoEy8M58tR2M9YxM+Z9RuQhqAx5q0CTmrruaP7Gveejg75hzd/5sg5nk9G3aLALEa3hE2FsmmTQ==", "cpu": [ "arm" ], @@ -2638,9 +2662,9 @@ ] }, "node_modules/@oxc-resolver/binding-linux-arm-musleabihf": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-11.20.0.tgz", - "integrity": "sha512-2ArPksaw0AqeuGBfoS715VF+JvJQAhD2niWgjE5hVO+L+nAfikVQopvngCMX9x4BD8itWoQ3dnikrQyl5Ho5Jg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-11.21.3.tgz", + "integrity": "sha512-quVoxFLBy43hWaQbbDtQNRwAX5vX76mv7n64icAtQcJ3eNgVeblqmkupF/hAneNthdqSlnd1sTjb3aQSaDPaCQ==", "cpu": [ "arm" ], @@ -2652,13 +2676,16 @@ ] }, "node_modules/@oxc-resolver/binding-linux-arm64-gnu": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-11.20.0.tgz", - "integrity": "sha512-0bJnmYFp62JdZ4nVMDUZ/C58BCZOCcqgKtnUlp7L9Ojf/czIN+3j72YlLPeWLkzlr6SlYvIQA4SGV/HyO0d+qg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-11.21.3.tgz", + "integrity": "sha512-X0AqNZgcD07Q4V3RDK18/vYOj/HQT/FnmEFGYS2jTWqY7JO13ryE3TEs3eAIgUJhBnNkpEaiXqz3VK8M7qQhWQ==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -2666,13 +2693,16 @@ ] }, "node_modules/@oxc-resolver/binding-linux-arm64-musl": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-musl/-/binding-linux-arm64-musl-11.20.0.tgz", - "integrity": "sha512-wKHHzPKZo7Ufhv/Bt6yxT7FOgnIgW4gwXcJUipkShGp68W3wGVqvr1Sr0fY65lN0Oy6y41+g2kIDvkgZaMMUkw==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-musl/-/binding-linux-arm64-musl-11.21.3.tgz", + "integrity": "sha512-YkaQnaKYdbuaXvRt5Qd0GpbihzVnyfR6z1SpYfIUC6RTu4NF7lDKPjVkYb+jRI2gedVO2rVpN35Y6akG6ud4Lw==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -2680,13 +2710,16 @@ ] }, "node_modules/@oxc-resolver/binding-linux-ppc64-gnu": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-11.20.0.tgz", - "integrity": "sha512-RN8goF7Ie0B79L4i4G6OeBocTgSC56vJbQ65VJje+oXnldVpLnOU7j/AQ/dP94TcCS+Yh6WG8u3Qt4ETteXFNQ==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-11.21.3.tgz", + "integrity": "sha512-gB9HwhrPiFqUzDeEq+y/CgAijz1YdI6BnXz5GaH2Pa9cWdutchlkGFAiAuGb/PjVQpiK6NFKzFuztxrweoit7A==", "cpu": [ "ppc64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -2694,13 +2727,16 @@ ] }, "node_modules/@oxc-resolver/binding-linux-riscv64-gnu": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-11.20.0.tgz", - "integrity": "sha512-5l1yU6/xQEqLZRzxqmMxJfWPslpwCmBsdDGaBvABPehxquCXDC7dd7oraNdKSJUMDXSM7VvVj8H2D2FTjU7oWw==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-11.21.3.tgz", + "integrity": "sha512-zjDWBlYk8QGv0H8dsPUWqkfjYIIjG2TvspGkzXL0eImbgxtZorA/klKeHyolevoT3Kvbi+1iMr9Lhrh7jf54Og==", "cpu": [ "riscv64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -2708,13 +2744,16 @@ ] }, "node_modules/@oxc-resolver/binding-linux-riscv64-musl": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-11.20.0.tgz", - "integrity": "sha512-xHEvkbgz6UC+A3JOyDQy76LkUaxsNSfIr3/GV8slwZsnuooJiIB34gzJfsyvR4JdCYNUUPsRJc/w/oWkODu+hg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-11.21.3.tgz", + "integrity": "sha512-4UfsQvacV388y1zpXL7C1x1FNYaV52JtuNRiuzrfQA2z1z6ElVrsidkGsrvQ5EgeSq1Pj7kaKqrgGkvFuxJ/tw==", "cpu": [ "riscv64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -2722,13 +2761,16 @@ ] }, "node_modules/@oxc-resolver/binding-linux-s390x-gnu": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-11.20.0.tgz", - "integrity": "sha512-aWPDUUmSeyHvlW+SoEUd+JIJsQhVhu6a5tBpDRMu058naPAchTgAVGCFy35zjbnFlt0i8hLWziff6HX0D3LU4g==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-11.21.3.tgz", + "integrity": "sha512-b5uH+HKH0MP5mNBYaK75SKsJbw52URqrx2LavYdq6wb0l3ExAG5niYRP9DWUNHdKilpaBVM2bXk9HNWrH3ew7Q==", "cpu": [ "s390x" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -2736,13 +2778,16 @@ ] }, "node_modules/@oxc-resolver/binding-linux-x64-gnu": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-gnu/-/binding-linux-x64-gnu-11.20.0.tgz", - "integrity": "sha512-x2YeSimvhJjKLVD8KSu8f/rqU1potcdEMkApIPJqjZWN7c2Fpt4g2X32WDg1p+XDAmyT7nuQGe0vnhvXeLbH+g==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-gnu/-/binding-linux-x64-gnu-11.21.3.tgz", + "integrity": "sha512-PjYlmilBpNRh2ntXNYAK3Am5w/nPfEpnU/96iNx7CI8EzAn12J4JRiec63wHJTH31nLoCNxBg/829pN+3CfG3Q==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -2750,13 +2795,16 @@ ] }, "node_modules/@oxc-resolver/binding-linux-x64-musl": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-musl/-/binding-linux-x64-musl-11.20.0.tgz", - "integrity": "sha512-kcRLEIxpZefeYfLChjpgFf3ilBzRDZ+yobMrpRsQlSrxuFGtm3U6PMU7AaEpMqo3NfDGVyJJseAjnRLzMFHjwQ==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-musl/-/binding-linux-x64-musl-11.21.3.tgz", + "integrity": "sha512-QTBAb7JuHlZ7JUEyM8UiQi2f7m/L4swBhP2TNpYIDc9Wp/wRw1G/8sl6i13aIzQAXH7LKIm294LeOHd0lQR8zA==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -2764,9 +2812,9 @@ ] }, "node_modules/@oxc-resolver/binding-openharmony-arm64": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-openharmony-arm64/-/binding-openharmony-arm64-11.20.0.tgz", - "integrity": "sha512-HHcfnApSZGtKhTiHqe8OZruOZe5XuFQH5/E0Yhj3u8fnFvzkM4/k6WjacUf4SvA0SPEAbfbgYmVPuo0VX/fIBQ==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-openharmony-arm64/-/binding-openharmony-arm64-11.21.3.tgz", + "integrity": "sha512-4j1DFwjwv36ec9kds0jU/ucQ5Ha4ERO/H95BxR5JFf0kqUUAJ1kwII7XhTc1vZrkdJkvLGC9Q2MbpObpum8RBg==", "cpu": [ "arm64" ], @@ -2778,9 +2826,9 @@ ] }, "node_modules/@oxc-resolver/binding-wasm32-wasi": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-wasm32-wasi/-/binding-wasm32-wasi-11.20.0.tgz", - "integrity": "sha512-Tn0y1XOFYHNfK1wp1Z5QK8Rcld/bsOwRISQXfqAZ5IBpv8Gz1IvV39fUWNprqNdRizgcvFhOzWwFun2zkJsyBg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-wasm32-wasi/-/binding-wasm32-wasi-11.21.3.tgz", + "integrity": "sha512-i8oluoel5kru/j1WNrjmQSiA3GQ7wvIYVR1IwIoZtKogAhya2iub+ZKIeSIkcJOrnzQ18Tzl/F+kL3fYOxZLvA==", "cpu": [ "wasm32" ], @@ -2788,18 +2836,41 @@ "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "1.10.0", - "@emnapi/runtime": "1.10.0", - "@napi-rs/wasm-runtime": "^1.1.4" + "@emnapi/core": "1.11.0", + "@emnapi/runtime": "1.11.0", + "@napi-rs/wasm-runtime": "^1.1.5" }, "engines": { "node": ">=14.0.0" } }, + "node_modules/@oxc-resolver/binding-wasm32-wasi/node_modules/@emnapi/core": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.0.tgz", + "integrity": "sha512-l9Oo58x0HOP5znGzVhYW9U3e5wVuA4LAZU2AGezTmkhO1CgQRFDhDg4nneHsu/t3WniXg9QrG2nIXL/ZS8ln8Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@oxc-resolver/binding-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.0.tgz", + "integrity": "sha512-55coeOFKHv1ywEcUXJtWU5f+Jr/W5tZDvZig8DLKSwUN1JpROQ4rk/SNOQiFWmaR/VKF4zuFyW1B8JduOSv6Pg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@oxc-resolver/binding-win32-arm64-msvc": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-11.20.0.tgz", - "integrity": "sha512-qPi25YNPe4YenS8MgsQU2+bIFHxxpLx1LVna2444cEHqNPhNjvWf9zqj4aWE43H9LpAsTmkkAlA3eL5ElBU3mA==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-11.21.3.tgz", + "integrity": "sha512-M/8dw8dD6aOs+NlPJax401CZB9I7Aut84isQLgALGGwke4Afvw+/7yYhZb94yXf6t2sPLhQLmSmtSV+2FhsOWg==", "cpu": [ "arm64" ], @@ -2811,9 +2882,9 @@ ] }, "node_modules/@oxc-resolver/binding-win32-x64-msvc": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-x64-msvc/-/binding-win32-x64-msvc-11.20.0.tgz", - "integrity": "sha512-Wb14jWEW8huH6It9F6sXd9vrYmIS7pMrgkU6sxpLxkP+9z+wRgs71hUEhRpcn8FOXAFa27FVWfY2tRpbfTzfLw==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-x64-msvc/-/binding-win32-x64-msvc-11.21.3.tgz", + "integrity": "sha512-H7BCt/VnS9hnmMp42eGhZ99izSCRvlnWwy/N71K1/J8QoExwY4262Z8QiEkMDtduRJrztayDxETTckmUuAVL9Q==", "cpu": [ "x64" ], @@ -4371,10 +4442,11 @@ } }, "node_modules/@testing-library/jest-dom": { - "version": "5.16.5", - "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.16.5.tgz", - "integrity": "sha512-N5ixQ2qKpi5OLYfwQmUb/5mSV9LneAcaUfp32pn4yCnpb8r/Yz0pXFPck21dIicKmi+ta5WRAknkZCfA8refMA==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.17.0.tgz", + "integrity": "sha512-ynmNeT7asXyH3aSVv4vvX4Rb+0qjOhdNHnO/3vuZNqPmhDpV/+rCSGwQ7bLcmU2cJ4dvoheIO85LQj0IbJHEtg==", "dev": true, + "license": "MIT", "dependencies": { "@adobe/css-tools": "^4.0.1", "@babel/runtime": "^7.9.2", @@ -4868,9 +4940,9 @@ } }, "node_modules/@tybys/wasm-util": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", - "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", "dev": true, "license": "MIT", "optional": true, @@ -5012,10 +5084,11 @@ "license": "MIT" }, "node_modules/@types/google.maps": { - "version": "3.51.0", - "resolved": "https://registry.npmjs.org/@types/google.maps/-/google.maps-3.51.0.tgz", - "integrity": "sha512-44/oQYjc5D6kxBcI3Qk9rk3IIOMwnlEMWDV7pwPJ2YI89s5Q1OzDrFvR7QJ3LFrpVXEhig+gyagFg54+foinFg==", - "dev": true + "version": "3.65.2", + "resolved": "https://registry.npmjs.org/@types/google.maps/-/google.maps-3.65.2.tgz", + "integrity": "sha512-e52bmOhGCQSNabFpL48iQlwJybq6rfns8NUVJ20MR7CdPlHQ2RmSCnPbJfrUYJfogrE4OiHQTZ4LXpop+eer1w==", + "dev": true, + "license": "MIT" }, "node_modules/@types/hast": { "version": "3.0.4", @@ -5115,10 +5188,11 @@ "license": "MIT" }, "node_modules/@types/latlon-geohash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/latlon-geohash/-/latlon-geohash-2.0.0.tgz", - "integrity": "sha512-bwTttcqf8StBA+ABJ4gxbgA+PgueUJGxjYzuWrlFfzDbIQd2FvOcE5l/0fY+BOKqmeWYAzjBQFdnKweI8KKNpA==", - "dev": true + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/latlon-geohash/-/latlon-geohash-2.0.4.tgz", + "integrity": "sha512-R/wb/V8lhhI0hyRGDTkT6F4C3lynkF/D29iMSN5B4bYswaa4R+wajgcTv9z73BotGArf4Q8BnDNtmLeI+WiKkQ==", + "dev": true, + "license": "MIT" }, "node_modules/@types/linkify-it": { "version": "5.0.0", @@ -5128,10 +5202,11 @@ "peer": true }, "node_modules/@types/lodash": { - "version": "4.14.191", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.191.tgz", - "integrity": "sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==", - "dev": true + "version": "4.17.24", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.24.tgz", + "integrity": "sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ==", + "dev": true, + "license": "MIT" }, "node_modules/@types/markdown-it": { "version": "14.1.2", @@ -5183,19 +5258,21 @@ } }, "node_modules/@types/react-beautiful-dnd": { - "version": "13.1.4", - "resolved": "https://registry.npmjs.org/@types/react-beautiful-dnd/-/react-beautiful-dnd-13.1.4.tgz", - "integrity": "sha512-4bIBdzOr0aavN+88q3C7Pgz+xkb7tz3whORYrmSj77wfVEMfiWiooIwVWFR7KM2e+uGTe5BVrXqSfb0aHeflJA==", + "version": "13.1.8", + "resolved": "https://registry.npmjs.org/@types/react-beautiful-dnd/-/react-beautiful-dnd-13.1.8.tgz", + "integrity": "sha512-E3TyFsro9pQuK4r8S/OL6G99eq7p8v29sX0PM7oT8Z+PJfZvSQTx4zTQbUJ+QZXioAF0e7TGBEcA1XhYhCweyQ==", "dev": true, + "license": "MIT", "dependencies": { "@types/react": "*" } }, "node_modules/@types/react-csv": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@types/react-csv/-/react-csv-1.1.3.tgz", - "integrity": "sha512-dkEdyRvRpygSnNg4cyzYWSUjukIQ5lAtXJwc7BqyUfzww/Cv2dcAFGYd+sWTFpGiDNZMVPp6vVPLcAPvJID8Kg==", + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@types/react-csv/-/react-csv-1.1.10.tgz", + "integrity": "sha512-PESAyASL7Nfi/IyBR3ufd8qZkyoS+7jOylKmJxRZUZLFASLo4NZaRsJ8rNP8pCcbIziADyWBbLPD1nPddhsL4g==", "dev": true, + "license": "MIT", "dependencies": { "@types/react": "*" } @@ -5230,10 +5307,11 @@ } }, "node_modules/@types/react-table": { - "version": "7.7.14", - "resolved": "https://registry.npmjs.org/@types/react-table/-/react-table-7.7.14.tgz", - "integrity": "sha512-TYrv7onCiakaG1uAu/UpQ9FojNEt/4/ht87EgJQaEGFoWV606ZLWUZAcUHzMxgc3v1mywP1cDyz3qB4ho3hWOw==", + "version": "7.7.20", + "resolved": "https://registry.npmjs.org/@types/react-table/-/react-table-7.7.20.tgz", + "integrity": "sha512-ahMp4pmjVlnExxNwxyaDrFgmKxSbPwU23sGQw2gJK4EhCvnvmib2s/O/+y1dfV57dXOwpr2plfyBol+vEHbi2w==", "dev": true, + "license": "MIT", "dependencies": { "@types/react": "*" } @@ -5278,20 +5356,20 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.57.2.tgz", - "integrity": "sha512-NZZgp0Fm2IkD+La5PR81sd+g+8oS6JwJje+aRWsDocxHkjyRw0J5L5ZTlN3LI1LlOcGL7ph3eaIUmTXMIjLk0w==", + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.64.0.tgz", + "integrity": "sha512-CGvQPBxN3wZLu6Rz2kFUpZeoCm78xUic92ck39KPePkO1NPOwjCqdQnm5Q87tpWw9vcBvW8XLrDXjH9PWYtJ3Q==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.57.2", - "@typescript-eslint/type-utils": "8.57.2", - "@typescript-eslint/utils": "8.57.2", - "@typescript-eslint/visitor-keys": "8.57.2", + "@typescript-eslint/scope-manager": "8.64.0", + "@typescript-eslint/type-utils": "8.64.0", + "@typescript-eslint/utils": "8.64.0", + "@typescript-eslint/visitor-keys": "8.64.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", - "ts-api-utils": "^2.4.0" + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5301,20 +5379,33 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.57.2", + "@typescript-eslint/parser": "^8.64.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.57.2.tgz", - "integrity": "sha512-snZKH+W4WbWkrBqj4gUNRIGb/jipDW3qMqVJ4C9rzdFc+wLwruxk+2a5D+uoFcKPAqyqEnSb4l2ULuZf95eSkw==", + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.65.0.tgz", + "integrity": "sha512-CZ4nMxWwgu1HEEFNkeaCptra9QCtkmKdgf3sWh1rl1trIhmxLilgTV4cwcbQ4wemnT4sWQN8CaKOmdYx+g2gMA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.57.2", - "@typescript-eslint/visitor-keys": "8.57.2" + "@typescript-eslint/scope-manager": "8.65.0", + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/typescript-estree": "8.65.0", + "@typescript-eslint/visitor-keys": "8.65.0", + "debug": "^4.4.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5322,14 +5413,22 @@ "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.2.tgz", - "integrity": "sha512-/iZM6FnM4tnx9csuTxspMW4BOSegshwX5oBDznJ7S4WggL7Vczz5d2W11ecc4vRrQMQHXRSxzrCsyG5EsPPTbA==", + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.65.0.tgz", + "integrity": "sha512-Esbl8OSYiVxBokYgWPf7VVWg/BE798wXhimnn9ML9Pt5qoDf8bfQlgjlKXR/k98+AcNzlLKYrpCcrcuZ9DZLgg==", "dev": true, "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/visitor-keys": "8.65.0" + }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -5338,22 +5437,15 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.2.tgz", - "integrity": "sha512-2MKM+I6g8tJxfSmFKOnHv2t8Sk3T6rF20A1Puk0svLK+uVapDZB/4pfAeB7nE83uAZrU6OxW+HmOd5wHVdXwXA==", + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.65.0.tgz", + "integrity": "sha512-8C71BQkGjiMmXtop7pHVJu1l2NNShFdkCyD6a2ezzs5vU/L3LRtb69EtcteFwz0mYMPzIgOw0n6OV4VBUWZd7A==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.57.2", - "@typescript-eslint/tsconfig-utils": "8.57.2", - "@typescript-eslint/types": "8.57.2", - "@typescript-eslint/visitor-keys": "8.57.2", - "debug": "^4.4.3", - "minimatch": "^10.2.2", - "semver": "^7.7.3", - "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.4.0" + "@typescript-eslint/types": "8.65.0", + "eslint-visitor-keys": "^5.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5361,22 +5453,31 @@ "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.57.2.tgz", - "integrity": "sha512-krRIbvPK1ju1WBKIefiX+bngPs+odIQUtR7kymzPfo1POVw3jlF+nLkmexdSSd4UCbDcQn+wMBATOOmpBbqgKg==", + "node_modules/@typescript-eslint/project-service": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.65.0.tgz", + "integrity": "sha512-SxnPhbTsGahizDgbu7oqFH/xVtzIqMd/s+WtnSxNxJZJpLbdT5IPdzg8EZxO3+PoKahXmwJLeNQOpKJb3/bi7Q==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.57.2", - "@typescript-eslint/types": "8.57.2", - "@typescript-eslint/typescript-estree": "8.57.2" + "@typescript-eslint/tsconfig-utils": "^8.65.0", + "@typescript-eslint/types": "^8.65.0", + "debug": "^4.4.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5386,19 +5487,18 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.2.tgz", - "integrity": "sha512-zhahknjobV2FiD6Ee9iLbS7OV9zi10rG26odsQdfBO/hjSzUQbkIYgda+iNKK1zNiW2ey+Lf8MU5btN17V3dUw==", + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.64.0.tgz", + "integrity": "sha512-CXEaFdYXjSTgKhisNkwCcJwTP8Pl+fmRrEQrri4nm3vU743bALrxzLmq7fHG/7e6a5xO0lDYeURpZmBuhHk54w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.57.2", - "eslint-visitor-keys": "^5.0.0" + "@typescript-eslint/types": "8.64.0", + "@typescript-eslint/visitor-keys": "8.64.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5408,80 +5508,131 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "node_modules/@typescript-eslint/scope-manager/node_modules/@typescript-eslint/types": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.64.0.tgz", + "integrity": "sha512-qjhfuTfLXjA4IOzXvz0rTjT01BqEiIgPoUeMwiEjnaHKJMTNo8rH5pYW1a2L/0Dnux2fPC85AeyJoWaGa8WxTA==", "dev": true, "license": "MIT", "engines": { - "node": "18 || 20 || >=22" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/brace-expansion": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", - "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.65.0.tgz", + "integrity": "sha512-j6GzGqCiRdA7Qhur2VVmKZAkBLfnHFQfx4TaJGL9RMveZqCo48jSHHO0DTgizEnGhtWnqmbtCUSrqSkdiY/0Hg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.64.0.tgz", + "integrity": "sha512-XWG4Fmmv/6SvyS9nH8jWrKs6terwJvE8cyRt1CzYYqzp9OrPhCT4cMc/f7C6RZCwG+qMmiffJS1/qJP8G1URtg==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^4.0.2" + "@typescript-eslint/types": "8.64.0", + "@typescript-eslint/typescript-estree": "8.64.0", + "@typescript-eslint/utils": "8.64.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" }, "engines": { - "node": "18 || 20 || >=22" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/eslint-visitor-keys": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", - "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/project-service": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.64.0.tgz", + "integrity": "sha512-tk4WpOJ6IEbGrVHaNmM0YRrwAD3exZlIK3iadQNAxh4YKk6jvUQ4ecq18n+v7+meh+cJ3j+D8nbk8sRKhlwLQg==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.64.0", + "@typescript-eslint/types": "^8.64.0", + "debug": "^4.4.3" + }, "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://opencollective.com/eslint" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.64.0.tgz", + "integrity": "sha512-2yo8rRNKuzbVWQp5kslhANqZ2uDAeROQHBRZNPu8JDsHmeFNj/XJJhX/FhNUWmkHHvoNsKa6+tHJiig87EzsQw==", "dev": true, "license": "MIT", "engines": { - "node": ">= 4" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/minimatch": { - "version": "10.2.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", - "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.64.0.tgz", + "integrity": "sha512-qjhfuTfLXjA4IOzXvz0rTjT01BqEiIgPoUeMwiEjnaHKJMTNo8rH5pYW1a2L/0Dnux2fPC85AeyJoWaGa8WxTA==", "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "brace-expansion": "^5.0.2" - }, + "license": "MIT", "engines": { - "node": "18 || 20 || >=22" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/parser": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.57.2.tgz", - "integrity": "sha512-30ScMRHIAD33JJQkgfGW1t8CURZtjc2JpTrq5n2HFhOefbAhb7ucc7xJwdWcrEtqUIYJ73Nybpsggii6GtAHjA==", + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.64.0.tgz", + "integrity": "sha512-Pztpsn1aCE1oWDvDEfUk31nngvvF7vUB5SwHFEaZIFpvw7WJtqUHHL4plBZDA9HfWJJjL13BdG0YrJInTUvoVA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.57.2", - "@typescript-eslint/types": "8.57.2", - "@typescript-eslint/typescript-estree": "8.57.2", - "@typescript-eslint/visitor-keys": "8.57.2", - "debug": "^4.4.3" + "@typescript-eslint/project-service": "8.64.0", + "@typescript-eslint/tsconfig-utils": "8.64.0", + "@typescript-eslint/types": "8.64.0", + "@typescript-eslint/visitor-keys": "8.64.0", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5491,32 +5642,52 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.57.2.tgz", - "integrity": "sha512-snZKH+W4WbWkrBqj4gUNRIGb/jipDW3qMqVJ4C9rzdFc+wLwruxk+2a5D+uoFcKPAqyqEnSb4l2ULuZf95eSkw==", + "node_modules/@typescript-eslint/type-utils/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/brace-expansion": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz", + "integrity": "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.57.2", - "@typescript-eslint/visitor-keys": "8.57.2" + "balanced-match": "^4.0.2" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "20 || >=22" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.2.tgz", - "integrity": "sha512-/iZM6FnM4tnx9csuTxspMW4BOSegshwX5oBDznJ7S4WggL7Vczz5d2W11ecc4vRrQMQHXRSxzrCsyG5EsPPTbA==", + "node_modules/@typescript-eslint/types": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.65.0.tgz", + "integrity": "sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg==", "dev": true, "license": "MIT", "engines": { @@ -5527,22 +5698,22 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.2.tgz", - "integrity": "sha512-2MKM+I6g8tJxfSmFKOnHv2t8Sk3T6rF20A1Puk0svLK+uVapDZB/4pfAeB7nE83uAZrU6OxW+HmOd5wHVdXwXA==", + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.65.0.tgz", + "integrity": "sha512-JboAE2swaYt4tb1fHhHTABE2K+OLy09XfcTbhnk4Pw96f9dd2e9iYsJ28gBggHlo5z5x1rkyWvcPoTuNTd4oGg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.57.2", - "@typescript-eslint/tsconfig-utils": "8.57.2", - "@typescript-eslint/types": "8.57.2", - "@typescript-eslint/visitor-keys": "8.57.2", + "@typescript-eslint/project-service": "8.65.0", + "@typescript-eslint/tsconfig-utils": "8.65.0", + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/visitor-keys": "8.65.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.4.0" + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5552,17 +5723,17 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.2.tgz", - "integrity": "sha512-zhahknjobV2FiD6Ee9iLbS7OV9zi10rG26odsQdfBO/hjSzUQbkIYgda+iNKK1zNiW2ey+Lf8MU5btN17V3dUw==", + "node_modules/@typescript-eslint/typescript-estree/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.65.0.tgz", + "integrity": "sha512-8C71BQkGjiMmXtop7pHVJu1l2NNShFdkCyD6a2ezzs5vU/L3LRtb69EtcteFwz0mYMPzIgOw0n6OV4VBUWZd7A==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/types": "8.65.0", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -5573,7 +5744,7 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/parser/node_modules/balanced-match": { + "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", @@ -5583,20 +5754,20 @@ "node": "18 || 20 || >=22" } }, - "node_modules/@typescript-eslint/parser/node_modules/brace-expansion": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", - "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz", + "integrity": "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, "engines": { - "node": "18 || 20 || >=22" + "node": "20 || >=22" } }, - "node_modules/@typescript-eslint/parser/node_modules/eslint-visitor-keys": { + "node_modules/@typescript-eslint/typescript-estree/node_modules/eslint-visitor-keys": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", @@ -5609,14 +5780,14 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/@typescript-eslint/parser/node_modules/minimatch": { - "version": "10.2.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", - "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^5.0.2" + "brace-expansion": "^5.0.5" }, "engines": { "node": "18 || 20 || >=22" @@ -5625,16 +5796,17 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@typescript-eslint/project-service": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.57.2.tgz", - "integrity": "sha512-FuH0wipFywXRTHf+bTTjNyuNQQsQC3qh/dYzaM4I4W0jrCqjCVuUh99+xd9KamUfmCGPvbO8NDngo/vsnNVqgw==", + "node_modules/@typescript-eslint/utils": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.64.0.tgz", + "integrity": "sha512-aJUGVB3+U0htrrCjoA8qukw8cm8fNCGAxK/tVoS70k8aeb7DETKeFozRiVFIwEeN9WJLsjaP3ph8I60tY2XZoQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.57.2", - "@typescript-eslint/types": "^8.57.2", - "debug": "^4.4.3" + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.64.0", + "@typescript-eslint/types": "8.64.0", + "@typescript-eslint/typescript-estree": "8.64.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5644,27 +5816,36 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/project-service/node_modules/@typescript-eslint/types": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.2.tgz", - "integrity": "sha512-/iZM6FnM4tnx9csuTxspMW4BOSegshwX5oBDznJ7S4WggL7Vczz5d2W11ecc4vRrQMQHXRSxzrCsyG5EsPPTbA==", + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/project-service": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.64.0.tgz", + "integrity": "sha512-tk4WpOJ6IEbGrVHaNmM0YRrwAD3exZlIK3iadQNAxh4YKk6jvUQ4ecq18n+v7+meh+cJ3j+D8nbk8sRKhlwLQg==", "dev": true, "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.64.0", + "@typescript-eslint/types": "^8.64.0", + "debug": "^4.4.3" + }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.2.tgz", - "integrity": "sha512-3Lm5DSM+DCowsUOJC+YqHHnKEfFh5CoGkj5Z31NQSNF4l5wdOwqGn99wmwN/LImhfY3KJnmordBq/4+VDe2eKw==", + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.64.0.tgz", + "integrity": "sha512-2yo8rRNKuzbVWQp5kslhANqZ2uDAeROQHBRZNPu8JDsHmeFNj/XJJhX/FhNUWmkHHvoNsKa6+tHJiig87EzsQw==", "dev": true, "license": "MIT", "engines": { @@ -5675,43 +5856,39 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/type-utils": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.57.2.tgz", - "integrity": "sha512-Co6ZCShm6kIbAM/s+oYVpKFfW7LBc6FXoPXjTRQ449PPNBY8U0KZXuevz5IFuuUj2H9ss40atTaf9dlGLzbWZg==", + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.64.0.tgz", + "integrity": "sha512-qjhfuTfLXjA4IOzXvz0rTjT01BqEiIgPoUeMwiEjnaHKJMTNo8rH5pYW1a2L/0Dnux2fPC85AeyJoWaGa8WxTA==", "dev": true, "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.57.2", - "@typescript-eslint/typescript-estree": "8.57.2", - "@typescript-eslint/utils": "8.57.2", - "debug": "^4.4.3", - "ts-api-utils": "^2.4.0" - }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/scope-manager": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.57.2.tgz", - "integrity": "sha512-snZKH+W4WbWkrBqj4gUNRIGb/jipDW3qMqVJ4C9rzdFc+wLwruxk+2a5D+uoFcKPAqyqEnSb4l2ULuZf95eSkw==", + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.64.0.tgz", + "integrity": "sha512-Pztpsn1aCE1oWDvDEfUk31nngvvF7vUB5SwHFEaZIFpvw7WJtqUHHL4plBZDA9HfWJJjL13BdG0YrJInTUvoVA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.57.2", - "@typescript-eslint/visitor-keys": "8.57.2" + "@typescript-eslint/project-service": "8.64.0", + "@typescript-eslint/tsconfig-utils": "8.64.0", + "@typescript-eslint/types": "8.64.0", + "@typescript-eslint/visitor-keys": "8.64.0", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5719,82 +5896,58 @@ "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.2.tgz", - "integrity": "sha512-/iZM6FnM4tnx9csuTxspMW4BOSegshwX5oBDznJ7S4WggL7Vczz5d2W11ecc4vRrQMQHXRSxzrCsyG5EsPPTbA==", + "node_modules/@typescript-eslint/utils/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "dev": true, "license": "MIT", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": "18 || 20 || >=22" } }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.2.tgz", - "integrity": "sha512-2MKM+I6g8tJxfSmFKOnHv2t8Sk3T6rF20A1Puk0svLK+uVapDZB/4pfAeB7nE83uAZrU6OxW+HmOd5wHVdXwXA==", + "node_modules/@typescript-eslint/utils/node_modules/brace-expansion": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz", + "integrity": "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.57.2", - "@typescript-eslint/tsconfig-utils": "8.57.2", - "@typescript-eslint/types": "8.57.2", - "@typescript-eslint/visitor-keys": "8.57.2", - "debug": "^4.4.3", - "minimatch": "^10.2.2", - "semver": "^7.7.3", - "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.4.0" + "balanced-match": "^4.0.2" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "node": "20 || >=22" } }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/utils": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.57.2.tgz", - "integrity": "sha512-krRIbvPK1ju1WBKIefiX+bngPs+odIQUtR7kymzPfo1POVw3jlF+nLkmexdSSd4UCbDcQn+wMBATOOmpBbqgKg==", + "node_modules/@typescript-eslint/utils/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "dev": true, - "license": "MIT", + "license": "BlueOak-1.0.0", "dependencies": { - "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.57.2", - "@typescript-eslint/types": "8.57.2", - "@typescript-eslint/typescript-estree": "8.57.2" + "brace-expansion": "^5.0.5" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "18 || 20 || >=22" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.2.tgz", - "integrity": "sha512-zhahknjobV2FiD6Ee9iLbS7OV9zi10rG26odsQdfBO/hjSzUQbkIYgda+iNKK1zNiW2ey+Lf8MU5btN17V3dUw==", + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.64.0.tgz", + "integrity": "sha512-mrtuL8Nsn6gi2H4mo5KMTp823M+3Q19Ew/i+Zlikq20tIMm99C3Ez0dCmkWWnxut20esQvTg8aUSEhMcAOXhEw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/types": "8.64.0", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -5805,30 +5958,21 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/type-utils/node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "node_modules/@typescript-eslint/visitor-keys/node_modules/@typescript-eslint/types": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.64.0.tgz", + "integrity": "sha512-qjhfuTfLXjA4IOzXvz0rTjT01BqEiIgPoUeMwiEjnaHKJMTNo8rH5pYW1a2L/0Dnux2fPC85AeyJoWaGa8WxTA==", "dev": true, "license": "MIT", "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/@typescript-eslint/type-utils/node_modules/brace-expansion": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", - "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^4.0.2" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, - "engines": { - "node": "18 || 20 || >=22" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/type-utils/node_modules/eslint-visitor-keys": { + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", @@ -5841,26 +5985,10 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/@typescript-eslint/type-utils/node_modules/minimatch": { - "version": "10.2.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", - "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "brace-expansion": "^5.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/@vitejs/plugin-react": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.1.4.tgz", - "integrity": "sha512-VIcFLdRi/VYRU8OL/puL7QXMYafHmqOnwTZY50U1JPlCNj30PxCMx65c494b1K9be9hX83KVt0+gTEwTWLqToA==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.2.0.tgz", + "integrity": "sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw==", "dev": true, "license": "MIT", "dependencies": { @@ -5875,7 +6003,7 @@ "node": "^20.19.0 || >=22.12.0" }, "peerDependencies": { - "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" } }, "node_modules/@vitejs/plugin-react/node_modules/react-refresh": { @@ -5889,31 +6017,31 @@ } }, "node_modules/@vitest/expect": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.0.tgz", - "integrity": "sha512-EIxG7k4wlWweuCLG9Y5InKFwpMEOyrMb6ZJ1ihYu02LVj/bzUwn2VMU+13PinsjRW75XnITeFrQBMH5+dLvCDA==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.10.tgz", + "integrity": "sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA==", "dev": true, "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", - "@vitest/spy": "4.1.0", - "@vitest/utils": "4.1.0", + "@vitest/spy": "4.1.10", + "@vitest/utils": "4.1.10", "chai": "^6.2.2", - "tinyrainbow": "^3.0.3" + "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/mocker": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.0.tgz", - "integrity": "sha512-evxREh+Hork43+Y4IOhTo+h5lGmVRyjqI739Rz4RlUPqwrkFFDF6EMvOOYjTx4E8Tl6gyCLRL8Mu7Ry12a13Tw==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.10.tgz", + "integrity": "sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "4.1.0", + "@vitest/spy": "4.1.10", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, @@ -5922,7 +6050,7 @@ }, "peerDependencies": { "msw": "^2.4.9", - "vite": "^6.0.0 || ^7.0.0 || ^8.0.0-0" + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "msw": { @@ -5934,26 +6062,26 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.0.tgz", - "integrity": "sha512-3RZLZlh88Ib0J7NQTRATfc/3ZPOnSUn2uDBUoGNn5T36+bALixmzphN26OUD3LRXWkJu4H0s5vvUeqBiw+kS0A==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.10.tgz", + "integrity": "sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==", "dev": true, "license": "MIT", "dependencies": { - "tinyrainbow": "^3.0.3" + "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/runner": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.0.tgz", - "integrity": "sha512-Duvx2OzQ7d6OjchL+trw+aSrb9idh7pnNfxrklo14p3zmNL4qPCDeIJAK+eBKYjkIwG96Bc6vYuxhqDXQOWpoQ==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.10.tgz", + "integrity": "sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "4.1.0", + "@vitest/utils": "4.1.10", "pathe": "^2.0.3" }, "funding": { @@ -5961,14 +6089,14 @@ } }, "node_modules/@vitest/snapshot": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.0.tgz", - "integrity": "sha512-0Vy9euT1kgsnj1CHttwi9i9o+4rRLEaPRSOJ5gyv579GJkNpgJK+B4HSv/rAWixx2wdAFci1X4CEPjiu2bXIMg==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.10.tgz", + "integrity": "sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.0", - "@vitest/utils": "4.1.0", + "@vitest/pretty-format": "4.1.10", + "@vitest/utils": "4.1.10", "magic-string": "^0.30.21", "pathe": "^2.0.3" }, @@ -5977,9 +6105,9 @@ } }, "node_modules/@vitest/spy": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.0.tgz", - "integrity": "sha512-pz77k+PgNpyMDv2FV6qmk5ZVau6c3R8HC8v342T2xlFxQKTrSeYw9waIJG8KgV9fFwAtTu4ceRzMivPTH6wSxw==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.10.tgz", + "integrity": "sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw==", "dev": true, "license": "MIT", "funding": { @@ -5987,15 +6115,15 @@ } }, "node_modules/@vitest/utils": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.0.tgz", - "integrity": "sha512-XfPXT6a8TZY3dcGY8EdwsBulFCIw+BeeX0RZn2x/BtiY/75YGh8FeWGG8QISN/WhaqSrE2OrlDgtF8q5uhOTmw==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.10.tgz", + "integrity": "sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.0", + "@vitest/pretty-format": "4.1.10", "convert-source-map": "^2.0.0", - "tinyrainbow": "^3.0.3" + "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" @@ -6407,9 +6535,9 @@ "integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==" }, "node_modules/autoprefixer": { - "version": "10.4.13", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.13.tgz", - "integrity": "sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg==", + "version": "10.5.4", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.5.4.tgz", + "integrity": "sha512-MaU0U/za7N3r6brxD4YB/l4NSrFzLPlANv6wEuQVaIPlD3L4W9rFcQPbL/EilY9BHhHvhfcz3gInDLrEtWT4EA==", "dev": true, "funding": [ { @@ -6419,14 +6547,18 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "browserslist": "^4.21.4", - "caniuse-lite": "^1.0.30001426", - "fraction.js": "^4.2.0", - "normalize-range": "^0.1.2", - "picocolors": "^1.0.0", + "browserslist": "^4.28.6", + "caniuse-lite": "^1.0.30001806", + "fraction.js": "^5.3.4", + "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "bin": { @@ -6461,13 +6593,16 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "node_modules/baseline-browser-mapping": { - "version": "2.9.19", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz", - "integrity": "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==", + "version": "2.11.5", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.5.tgz", + "integrity": "sha512-xJo6a6YZnwZfnyGmQKWMbVOcii7XRibjOskRh+WJ9UHQoX16xrQrcIgAMQOzfvs8XiLMx6ih/fsLPF73iY2D1A==", "dev": true, "license": "Apache-2.0", "bin": { - "baseline-browser-mapping": "dist/cli.js" + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" } }, "node_modules/better-opn": { @@ -6547,9 +6682,9 @@ } }, "node_modules/browserslist": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", - "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "version": "4.28.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.7.tgz", + "integrity": "sha512-JxV13hNrFxqjOc8alRbq9dK1MM79NEXYpma2B2J4wAtpWS5zIEIKqWPGCl7N4o7Uc7B7itylh7SuDujATRyyTw==", "dev": true, "funding": [ { @@ -6567,11 +6702,11 @@ ], "license": "MIT", "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" + "baseline-browser-mapping": "^2.10.44", + "caniuse-lite": "^1.0.30001806", + "electron-to-chromium": "^1.5.393", + "node-releases": "^2.0.51", + "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" @@ -6660,9 +6795,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001767", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001767.tgz", - "integrity": "sha512-34+zUAMhSH+r+9eKmYG+k2Rpt8XttfE4yXAjoZvkAPs15xcYQhyBYdalJ65BzivAvGRMViEjy6oKr/S91loekQ==", + "version": "1.0.30001806", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001806.tgz", + "integrity": "sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==", "dev": true, "funding": [ { @@ -7303,9 +7438,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.286", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz", - "integrity": "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==", + "version": "1.5.396", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.396.tgz", + "integrity": "sha512-yHiw2Y3C3H9U6TMbOfoWK/BPreiOPXRfTWPBwQBoZG6/8TB6eOPnsy5oaRYuatR7Fw2SJ4kKforgufeo7fq0EQ==", "dev": true, "license": "ISC" }, @@ -7603,9 +7738,9 @@ } }, "node_modules/eslint": { - "version": "9.39.4", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", - "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", + "version": "9.39.5", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.5.tgz", + "integrity": "sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==", "dev": true, "license": "MIT", "dependencies": { @@ -7614,8 +7749,8 @@ "@eslint/config-array": "^0.21.2", "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", - "@eslint/eslintrc": "^3.3.5", - "@eslint/js": "9.39.4", + "@eslint/eslintrc": "^3.3.6", + "@eslint/js": "9.39.5", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", @@ -7700,9 +7835,9 @@ } }, "node_modules/eslint-plugin-react-hooks": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz", - "integrity": "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.1.1.tgz", + "integrity": "sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==", "dev": true, "license": "MIT", "dependencies": { @@ -7716,7 +7851,7 @@ "node": ">=18" }, "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0" } }, "node_modules/eslint-plugin-react/node_modules/doctrine": { @@ -8228,16 +8363,17 @@ } }, "node_modules/fraction.js": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", - "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==", + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", + "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", "dev": true, + "license": "MIT", "engines": { "node": "*" }, "funding": { - "type": "patreon", - "url": "https://www.patreon.com/infusion" + "type": "github", + "url": "https://github.com/sponsors/rawify" } }, "node_modules/fs.realpath": { @@ -9524,10 +9660,20 @@ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", + "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -9674,9 +9820,9 @@ } }, "node_modules/knip": { - "version": "6.16.1", - "resolved": "https://registry.npmjs.org/knip/-/knip-6.16.1.tgz", - "integrity": "sha512-TKMn1rxgH6h9vXR9Y0B+Cq7AdPTr9EI02IwoT65NzqYUkvoDQAaJ/aPybiFpAhZ1px6cNYYwXf86iHkBgzCo9w==", + "version": "6.27.0", + "resolved": "https://registry.npmjs.org/knip/-/knip-6.27.0.tgz", + "integrity": "sha512-CngYEYrD0n20N06FXA8n3u/0Wnnugoa+B9k14OP+iKIgkCHuzvIdsP3nfwjhByoc1WfogpxfiriMboAXFETDUw==", "dev": true, "funding": [ { @@ -9694,13 +9840,13 @@ "formatly": "^0.3.0", "get-tsconfig": "4.14.0", "jiti": "^2.7.0", - "oxc-parser": "^0.133.0", - "oxc-resolver": "^11.20.0", + "oxc-parser": "^0.137.0", + "oxc-resolver": "11.21.3", "picomatch": "^4.0.4", "smol-toml": "^1.6.1", "strip-json-comments": "5.0.3", - "tinyglobby": "^0.2.16", - "unbash": "^3.0.0", + "tinyglobby": "^0.2.17", + "unbash": "^4.0.1", "yaml": "^2.9.0", "zod": "^4.1.11" }, @@ -10170,11 +10316,14 @@ } }, "node_modules/node-releases": { - "version": "2.0.27", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", - "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "version": "2.0.51", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.51.tgz", + "integrity": "sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=18" + } }, "node_modules/normalize-path": { "version": "3.0.0", @@ -10184,15 +10333,6 @@ "node": ">=0.10.0" } }, - "node_modules/normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -10404,13 +10544,13 @@ } }, "node_modules/oxc-parser": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/oxc-parser/-/oxc-parser-0.133.0.tgz", - "integrity": "sha512-661RSx+ZcjBmjBYid+Fpp/2F5EbtildpeoZh5HdgnGs+jZ03nqQEQW8yGkt4BGyOC3OMPDQQRl8M5kqD2/g6jw==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/oxc-parser/-/oxc-parser-0.137.0.tgz", + "integrity": "sha512-yFImD+WLElJpLKy8llG1qe4DCmMsL18peRp8XP1JKfig/gISbJkglnpDtX2aTmAn10kZF7164HbN2H8QPsXxGg==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "^0.133.0" + "@oxc-project/types": "^0.137.0" }, "engines": { "node": "^20.19.0 || >=22.12.0" @@ -10419,57 +10559,57 @@ "url": "https://github.com/sponsors/Boshen" }, "optionalDependencies": { - "@oxc-parser/binding-android-arm-eabi": "0.133.0", - "@oxc-parser/binding-android-arm64": "0.133.0", - "@oxc-parser/binding-darwin-arm64": "0.133.0", - "@oxc-parser/binding-darwin-x64": "0.133.0", - "@oxc-parser/binding-freebsd-x64": "0.133.0", - "@oxc-parser/binding-linux-arm-gnueabihf": "0.133.0", - "@oxc-parser/binding-linux-arm-musleabihf": "0.133.0", - "@oxc-parser/binding-linux-arm64-gnu": "0.133.0", - "@oxc-parser/binding-linux-arm64-musl": "0.133.0", - "@oxc-parser/binding-linux-ppc64-gnu": "0.133.0", - "@oxc-parser/binding-linux-riscv64-gnu": "0.133.0", - "@oxc-parser/binding-linux-riscv64-musl": "0.133.0", - "@oxc-parser/binding-linux-s390x-gnu": "0.133.0", - "@oxc-parser/binding-linux-x64-gnu": "0.133.0", - "@oxc-parser/binding-linux-x64-musl": "0.133.0", - "@oxc-parser/binding-openharmony-arm64": "0.133.0", - "@oxc-parser/binding-wasm32-wasi": "0.133.0", - "@oxc-parser/binding-win32-arm64-msvc": "0.133.0", - "@oxc-parser/binding-win32-ia32-msvc": "0.133.0", - "@oxc-parser/binding-win32-x64-msvc": "0.133.0" + "@oxc-parser/binding-android-arm-eabi": "0.137.0", + "@oxc-parser/binding-android-arm64": "0.137.0", + "@oxc-parser/binding-darwin-arm64": "0.137.0", + "@oxc-parser/binding-darwin-x64": "0.137.0", + "@oxc-parser/binding-freebsd-x64": "0.137.0", + "@oxc-parser/binding-linux-arm-gnueabihf": "0.137.0", + "@oxc-parser/binding-linux-arm-musleabihf": "0.137.0", + "@oxc-parser/binding-linux-arm64-gnu": "0.137.0", + "@oxc-parser/binding-linux-arm64-musl": "0.137.0", + "@oxc-parser/binding-linux-ppc64-gnu": "0.137.0", + "@oxc-parser/binding-linux-riscv64-gnu": "0.137.0", + "@oxc-parser/binding-linux-riscv64-musl": "0.137.0", + "@oxc-parser/binding-linux-s390x-gnu": "0.137.0", + "@oxc-parser/binding-linux-x64-gnu": "0.137.0", + "@oxc-parser/binding-linux-x64-musl": "0.137.0", + "@oxc-parser/binding-openharmony-arm64": "0.137.0", + "@oxc-parser/binding-wasm32-wasi": "0.137.0", + "@oxc-parser/binding-win32-arm64-msvc": "0.137.0", + "@oxc-parser/binding-win32-ia32-msvc": "0.137.0", + "@oxc-parser/binding-win32-x64-msvc": "0.137.0" } }, "node_modules/oxc-resolver": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/oxc-resolver/-/oxc-resolver-11.20.0.tgz", - "integrity": "sha512-CblytBiV/a/ZXY34dsVU2NxhIOxMXst8CvDCtyBelVITgd7PLrKzbEbA6oKLdPjvDKDzCiW48qzmzZ+mYaqn+g==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/oxc-resolver/-/oxc-resolver-11.21.3.tgz", + "integrity": "sha512-2Mx3fKQz7+xgrBONjsxOgCGtMHOn38/HxMzW1I5efwXB5a4lRN0Vp40gYUJFBWJslcrvwoofTrqoTnLbwTd3pA==", "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/Boshen" }, "optionalDependencies": { - "@oxc-resolver/binding-android-arm-eabi": "11.20.0", - "@oxc-resolver/binding-android-arm64": "11.20.0", - "@oxc-resolver/binding-darwin-arm64": "11.20.0", - "@oxc-resolver/binding-darwin-x64": "11.20.0", - "@oxc-resolver/binding-freebsd-x64": "11.20.0", - "@oxc-resolver/binding-linux-arm-gnueabihf": "11.20.0", - "@oxc-resolver/binding-linux-arm-musleabihf": "11.20.0", - "@oxc-resolver/binding-linux-arm64-gnu": "11.20.0", - "@oxc-resolver/binding-linux-arm64-musl": "11.20.0", - "@oxc-resolver/binding-linux-ppc64-gnu": "11.20.0", - "@oxc-resolver/binding-linux-riscv64-gnu": "11.20.0", - "@oxc-resolver/binding-linux-riscv64-musl": "11.20.0", - "@oxc-resolver/binding-linux-s390x-gnu": "11.20.0", - "@oxc-resolver/binding-linux-x64-gnu": "11.20.0", - "@oxc-resolver/binding-linux-x64-musl": "11.20.0", - "@oxc-resolver/binding-openharmony-arm64": "11.20.0", - "@oxc-resolver/binding-wasm32-wasi": "11.20.0", - "@oxc-resolver/binding-win32-arm64-msvc": "11.20.0", - "@oxc-resolver/binding-win32-x64-msvc": "11.20.0" + "@oxc-resolver/binding-android-arm-eabi": "11.21.3", + "@oxc-resolver/binding-android-arm64": "11.21.3", + "@oxc-resolver/binding-darwin-arm64": "11.21.3", + "@oxc-resolver/binding-darwin-x64": "11.21.3", + "@oxc-resolver/binding-freebsd-x64": "11.21.3", + "@oxc-resolver/binding-linux-arm-gnueabihf": "11.21.3", + "@oxc-resolver/binding-linux-arm-musleabihf": "11.21.3", + "@oxc-resolver/binding-linux-arm64-gnu": "11.21.3", + "@oxc-resolver/binding-linux-arm64-musl": "11.21.3", + "@oxc-resolver/binding-linux-ppc64-gnu": "11.21.3", + "@oxc-resolver/binding-linux-riscv64-gnu": "11.21.3", + "@oxc-resolver/binding-linux-riscv64-musl": "11.21.3", + "@oxc-resolver/binding-linux-s390x-gnu": "11.21.3", + "@oxc-resolver/binding-linux-x64-gnu": "11.21.3", + "@oxc-resolver/binding-linux-x64-musl": "11.21.3", + "@oxc-resolver/binding-openharmony-arm64": "11.21.3", + "@oxc-resolver/binding-wasm32-wasi": "11.21.3", + "@oxc-resolver/binding-win32-arm64-msvc": "11.21.3", + "@oxc-resolver/binding-win32-x64-msvc": "11.21.3" } }, "node_modules/p-limit": { @@ -14017,10 +14157,11 @@ } }, "node_modules/typescript": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.2.tgz", - "integrity": "sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -14037,9 +14178,9 @@ "peer": true }, "node_modules/unbash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unbash/-/unbash-3.0.0.tgz", - "integrity": "sha512-FeFPZ/WFT0mbRCuydiZzpPFlrYN8ZUpphQKoq4EeElVIYjYyGzPMxQR/simUwCOJIyVhpFk4RbtyO7RuMpMnHA==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/unbash/-/unbash-4.0.4.tgz", + "integrity": "sha512-60m9IVGbavD6jholbxt0jVBXZkEB/HsMZq7Tyaghseve2/Sf0zQRAIfWsD34sde+DKP2tBxJS2wP88ZM0D1FhA==", "dev": true, "license": "ISC", "engines": { @@ -14332,13 +14473,13 @@ } }, "node_modules/vite": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.5.tgz", - "integrity": "sha512-KuOaNhcnGFN2zIPGA7wRmzF+lJA1sea7rHq17aiJ++9lzY1WWG6Jpwqwe1KNbRVPIqHmr8GLYx7jbrQcN/7/ww==", + "version": "7.3.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.6.tgz", + "integrity": "sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg==", "dev": true, "license": "MIT", "dependencies": { - "esbuild": "^0.27.0", + "esbuild": "^0.27.0 || ^0.28.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", @@ -14758,19 +14899,19 @@ } }, "node_modules/vitest": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.0.tgz", - "integrity": "sha512-YbDrMF9jM2Lqc++2530UourxZHmkKLxrs4+mYhEwqWS97WJ7wOYEkcr+QfRgJ3PW9wz3odRijLZjHEaRLTNbqw==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.10.tgz", + "integrity": "sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "4.1.0", - "@vitest/mocker": "4.1.0", - "@vitest/pretty-format": "4.1.0", - "@vitest/runner": "4.1.0", - "@vitest/snapshot": "4.1.0", - "@vitest/spy": "4.1.0", - "@vitest/utils": "4.1.0", + "@vitest/expect": "4.1.10", + "@vitest/mocker": "4.1.10", + "@vitest/pretty-format": "4.1.10", + "@vitest/runner": "4.1.10", + "@vitest/snapshot": "4.1.10", + "@vitest/spy": "4.1.10", + "@vitest/utils": "4.1.10", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", @@ -14781,8 +14922,8 @@ "tinybench": "^2.9.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", - "tinyrainbow": "^3.0.3", - "vite": "^6.0.0 || ^7.0.0 || ^8.0.0-0", + "tinyrainbow": "^3.1.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", "why-is-node-running": "^2.3.0" }, "bin": { @@ -14798,13 +14939,15 @@ "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "4.1.0", - "@vitest/browser-preview": "4.1.0", - "@vitest/browser-webdriverio": "4.1.0", - "@vitest/ui": "4.1.0", + "@vitest/browser-playwright": "4.1.10", + "@vitest/browser-preview": "4.1.10", + "@vitest/browser-webdriverio": "4.1.10", + "@vitest/coverage-istanbul": "4.1.10", + "@vitest/coverage-v8": "4.1.10", + "@vitest/ui": "4.1.10", "happy-dom": "*", "jsdom": "*", - "vite": "^6.0.0 || ^7.0.0 || ^8.0.0-0" + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "@edge-runtime/vm": { @@ -14825,6 +14968,12 @@ "@vitest/browser-webdriverio": { "optional": true }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { + "optional": true + }, "@vitest/ui": { "optional": true }, diff --git a/client/package.json b/client/package.json index 5d213b73..39b5e44b 100644 --- a/client/package.json +++ b/client/package.json @@ -74,44 +74,44 @@ "web-vitals": "^1.0.1" }, "devDependencies": { - "@eslint/compat": "^2.0.3", - "@eslint/eslintrc": "^3.3.5", + "@eslint/compat": "^2.1.0", + "@eslint/eslintrc": "^3.3.6", "@faker-js/faker": "^8.4.1", "@storybook/react": "^9.1.20", "@storybook/react-vite": "^9.1.20", - "@testing-library/jest-dom": "^5.11.9", + "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^11.2.3", "@testing-library/user-event": "^12.6.0", - "@types/google.maps": "^3.48.7", - "@types/latlon-geohash": "^2.0.0", - "@types/lodash": "^4.14.181", + "@types/google.maps": "^3.65.2", + "@types/latlon-geohash": "^2.0.4", + "@types/lodash": "^4.17.24", "@types/react": "^18.0.0", - "@types/react-beautiful-dnd": "^13.1.4", - "@types/react-csv": "^1.1.3", + "@types/react-beautiful-dnd": "^13.1.8", + "@types/react-csv": "^1.1.10", "@types/react-dom": "^18.0.0", "@types/react-syntax-highlighter": "^13.5.2", - "@types/react-table": "^7.7.5", - "@typescript-eslint/eslint-plugin": "^8.57.2", + "@types/react-table": "^7.7.20", + "@typescript-eslint/eslint-plugin": "^8.64.0", "@typescript-eslint/parser": "^8.57.2", - "@vitejs/plugin-react": "^5.1.4", - "autoprefixer": "^10.4.7", - "eslint": "^9.39.4", + "@vitejs/plugin-react": "^5.2.0", + "autoprefixer": "^10.5.4", + "eslint": "^9.39.5", "eslint-plugin-custom-rules": "file:./eslint", "eslint-plugin-react": "^7.30.1", - "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-react-hooks": "^7.1.1", "jsdom": "^29.1.1", - "knip": "^6.16.1", + "knip": "^6.27.0", "postcss": "^8.5.23", "prop-types": "^15.8.1", "source-map-explorer": "^2.5.3", "storybook": "^9.1.20", "tailwindcss": "^3.4.19", - "typescript": "^5.3.2", - "vite": "^7.3.5", + "typescript": "^5.9.3", + "vite": "^7.3.6", "vite-plugin-commonjs": "^0.10.4", "vite-plugin-svgr": "^4.5.0", "vite-tsconfig-paths": "^6.1.1", - "vitest": "^4.1.0" + "vitest": "^4.1.10" }, "overrides": { "tailwindcss": { diff --git a/client/src/utils/collections.ts b/client/src/utils/collections.ts index 4bf43da3..3071b6cc 100644 --- a/client/src/utils/collections.ts +++ b/client/src/utils/collections.ts @@ -22,6 +22,8 @@ export function filterNullOrUndefined( return array.filter((it) => it !== null && it !== undefined) as T[]; } -export function arrayFromArrayOrSingleItem(array: readonly T[] | T): T[] { - return Array.isArray(array) ? [...array] : [array]; +export function arrayFromArrayOrSingleItem( + array: readonly string[] | string, +): string[] { + return typeof array === 'string' ? [array] : [...array]; } diff --git a/client/src/webpages/dashboard/investigation/ItemInvestigation.tsx b/client/src/webpages/dashboard/investigation/ItemInvestigation.tsx index 7526c912..416c1fd8 100644 --- a/client/src/webpages/dashboard/investigation/ItemInvestigation.tsx +++ b/client/src/webpages/dashboard/investigation/ItemInvestigation.tsx @@ -403,7 +403,9 @@ export default function ItemInvestigation(props: { try { return getFieldValueForRole( { - // eslint-disable-next-line custom-rules/no-casting-in-getFieldValueForRole + // The GraphQL result is an item-type union, but the generic helper + // requires one compatible schema-role type at this call site. + // eslint-disable-next-line custom-rules/no-casting-in-getFieldValueForRole, @typescript-eslint/no-unnecessary-type-assertion type: item.type as Parameters< typeof getFieldValueForRole >[0]['type'], diff --git a/client/src/webpages/dashboard/item_types/ItemTypePreview.tsx b/client/src/webpages/dashboard/item_types/ItemTypePreview.tsx index 8621e3d1..49a742f7 100644 --- a/client/src/webpages/dashboard/item_types/ItemTypePreview.tsx +++ b/client/src/webpages/dashboard/item_types/ItemTypePreview.tsx @@ -37,7 +37,7 @@ export default function ItemTypePreview(props: { case 'CONTENT': return } />; case 'THREAD': - return } />; + return ; case 'USER': return } />; } diff --git a/client/src/webpages/dashboard/mrt/manual_review_job/v2/ncmec/NCMECReviewUser.tsx b/client/src/webpages/dashboard/mrt/manual_review_job/v2/ncmec/NCMECReviewUser.tsx index 037a4316..02b3c325 100644 --- a/client/src/webpages/dashboard/mrt/manual_review_job/v2/ncmec/NCMECReviewUser.tsx +++ b/client/src/webpages/dashboard/mrt/manual_review_job/v2/ncmec/NCMECReviewUser.tsx @@ -447,7 +447,7 @@ export default function NCMECReviewUser( } const { - moderatorSafetyBlurLevel = 2 as BlurStrength, + moderatorSafetyBlurLevel = 2, moderatorSafetyGrayscale = true, moderatorSafetyMuteVideo = true, moderatorSafetySepia = false, diff --git a/client/src/webpages/dashboard/rules/rule_form/RuleForm.tsx b/client/src/webpages/dashboard/rules/rule_form/RuleForm.tsx index c9ad7f6c..e577412c 100644 --- a/client/src/webpages/dashboard/rules/rule_form/RuleForm.tsx +++ b/client/src/webpages/dashboard/rules/rule_form/RuleForm.tsx @@ -685,7 +685,7 @@ export default function RuleForm() { const out: Record> = {}; for (const action of rule?.actions ?? []) { if ('configuredParameters' in action && action.configuredParameters) { - out[action.id] = action.configuredParameters as Record; + out[action.id] = action.configuredParameters; } } return out; diff --git a/client/src/webpages/dashboard/rules/rule_form/RuleFormReducers.test.ts b/client/src/webpages/dashboard/rules/rule_form/RuleFormReducers.test.ts index e38331d8..a9f9b752 100644 --- a/client/src/webpages/dashboard/rules/rule_form/RuleFormReducers.test.ts +++ b/client/src/webpages/dashboard/rules/rule_form/RuleFormReducers.test.ts @@ -7,7 +7,7 @@ import { GQLSignalPricingStructureType, GQLSignalType, } from '../../../../graphql/generated'; -import { RuleFormConditionSet, RuleFormLeafCondition } from '../types'; +import { RuleFormLeafCondition } from '../types'; import { initialState, RuleFormState } from './RuleForm'; import { RuleFormReducerActionType, updateInput } from './RuleFormReducers'; import { @@ -86,7 +86,7 @@ describe('updateInput signal eligibility', () => { conditions: [ { input: oldInput, signal: custom1, eligibleSignals: [custom1] }, ], - } as RuleFormConditionSet, + }, }; const result = updateInput(state, { @@ -117,7 +117,7 @@ describe('updateInput signal eligibility', () => { conditions: [ { input: oldInput, signal: custom2, eligibleSignals: [custom2] }, ], - } as RuleFormConditionSet, + }, }; const result = updateInput(state, { From f23beb87122c284c405b4a9f23bb0f932d56a76d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Jul 2026 13:02:18 +0100 Subject: [PATCH 41/57] build(deps-dev): bump the server-dev group across 1 directory with 23 updates (#948) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * build(deps-dev): bump the server-dev group across 1 directory with 25 updates Bumps the server-dev group with 24 updates in the /server directory: | Package | From | To | | --- | --- | --- | | [@eslint/compat](https://github.com/eslint/rewrite/tree/HEAD/packages/compat) | `2.0.3` | `2.1.0` | | [@eslint/eslintrc](https://github.com/eslint/eslintrc) | `3.3.5` | `3.3.6` | | [@eslint/js](https://github.com/eslint/eslint/tree/HEAD/packages/js) | `9.39.4` | `9.39.5` | | [@playwright/test](https://github.com/microsoft/playwright) | `1.61.0` | `1.61.1` | | [@types/cookie-parser](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/cookie-parser) | `1.4.3` | `1.4.10` | | [@types/debug](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/debug) | `4.1.12` | `4.1.13` | | [@types/express-serve-static-core](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/express-serve-static-core) | `5.1.1` | `5.1.2` | | [@types/js-yaml](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/js-yaml) | `4.0.5` | `4.0.9` | | [@types/lodash](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/lodash) | `4.14.198` | `4.17.24` | | [@types/morgan](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/morgan) | `1.9.4` | `1.9.10` | | [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `18.17.3` | `18.19.130` | | [@types/validator](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/validator) | `13.9.0` | `13.15.10` | | [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) | `8.59.0` | `8.65.0` | | [eslint](https://github.com/eslint/eslint) | `9.39.4` | `9.39.5` | | [eslint-import-resolver-typescript](https://github.com/import-js/eslint-import-resolver-typescript) | `3.6.0` | `3.10.1` | | [eslint-plugin-functional](https://github.com/eslint-functional/eslint-plugin-functional) | `9.0.4` | `9.0.5` | | [eslint-plugin-jsdoc](https://github.com/gajus/eslint-plugin-jsdoc) | `62.5.4` | `62.9.0` | | [eslint-plugin-promise](https://github.com/eslint-community/eslint-plugin-promise) | `7.2.1` | `7.3.0` | | [eslint-plugin-switch-statement](https://github.com/ethanresnick/eslint-plugin-exhaustive-switch) | `0.0.11` | `0.0.12` | | [jest-light-runner](https://github.com/nicolo-ribaudo/jest-light-runner) | `0.4.1` | `0.8.1` | | [knip](https://github.com/webpro-nl/knip/tree/HEAD/packages/knip) | `6.16.1` | `6.27.0` | | [ts-node](https://github.com/TypeStrong/ts-node) | `10.9.1` | `10.9.2` | | [typescript](https://github.com/microsoft/TypeScript) | `5.5.2` | `5.9.3` | | [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) | `8.57.2` | `8.65.0` | Updates `@eslint/compat` from 2.0.3 to 2.1.0 - [Release notes](https://github.com/eslint/rewrite/releases) - [Changelog](https://github.com/eslint/rewrite/blob/main/packages/compat/CHANGELOG.md) - [Commits](https://github.com/eslint/rewrite/commits/compat-v2.1.0/packages/compat) Updates `@eslint/eslintrc` from 3.3.5 to 3.3.6 - [Release notes](https://github.com/eslint/eslintrc/releases) - [Changelog](https://github.com/eslint/eslintrc/blob/main/CHANGELOG.md) - [Commits](https://github.com/eslint/eslintrc/compare/eslintrc-v3.3.5...eslintrc-v3.3.6) Updates `@eslint/js` from 9.39.4 to 9.39.5 - [Release notes](https://github.com/eslint/eslint/releases) - [Commits](https://github.com/eslint/eslint/commits/v9.39.5/packages/js) Updates `@playwright/test` from 1.61.0 to 1.61.1 - [Release notes](https://github.com/microsoft/playwright/releases) - [Commits](https://github.com/microsoft/playwright/compare/v1.61.0...v1.61.1) Updates `@types/cookie-parser` from 1.4.3 to 1.4.10 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/cookie-parser) Updates `@types/debug` from 4.1.12 to 4.1.13 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/debug) Updates `@types/express-serve-static-core` from 5.1.1 to 5.1.2 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/express-serve-static-core) Updates `@types/js-yaml` from 4.0.5 to 4.0.9 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/js-yaml) Updates `@types/lodash` from 4.14.198 to 4.17.24 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/lodash) Updates `@types/morgan` from 1.9.4 to 1.9.10 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/morgan) Updates `@types/node` from 18.17.3 to 18.19.130 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) Updates `@types/validator` from 13.9.0 to 13.15.10 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/validator) Updates `@typescript-eslint/eslint-plugin` from 8.59.0 to 8.65.0 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.65.0/packages/eslint-plugin) Updates `@typescript-eslint/parser` from 8.59.0 to 8.65.0 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.65.0/packages/parser) Updates `eslint` from 9.39.4 to 9.39.5 - [Release notes](https://github.com/eslint/eslint/releases) - [Commits](https://github.com/eslint/eslint/compare/v9.39.4...v9.39.5) Updates `eslint-import-resolver-typescript` from 3.6.0 to 3.10.1 - [Release notes](https://github.com/import-js/eslint-import-resolver-typescript/releases) - [Changelog](https://github.com/import-js/eslint-import-resolver-typescript/blob/v3.10.1/CHANGELOG.md) - [Commits](https://github.com/import-js/eslint-import-resolver-typescript/compare/v3.6.0...v3.10.1) Updates `eslint-plugin-functional` from 9.0.4 to 9.0.5 - [Release notes](https://github.com/eslint-functional/eslint-plugin-functional/releases) - [Changelog](https://github.com/eslint-functional/eslint-plugin-functional/blob/main/CHANGELOG.md) - [Commits](https://github.com/eslint-functional/eslint-plugin-functional/compare/v9.0.4...v9.0.5) Updates `eslint-plugin-jsdoc` from 62.5.4 to 62.9.0 - [Release notes](https://github.com/gajus/eslint-plugin-jsdoc/releases) - [Commits](https://github.com/gajus/eslint-plugin-jsdoc/compare/v62.5.4...v62.9.0) Updates `eslint-plugin-promise` from 7.2.1 to 7.3.0 - [Release notes](https://github.com/eslint-community/eslint-plugin-promise/releases) - [Changelog](https://github.com/eslint-community/eslint-plugin-promise/blob/main/CHANGELOG.md) - [Commits](https://github.com/eslint-community/eslint-plugin-promise/compare/v7.2.1...v7.3.0) Updates `eslint-plugin-switch-statement` from 0.0.11 to 0.0.12 - [Commits](https://github.com/ethanresnick/eslint-plugin-exhaustive-switch/commits) Updates `jest-light-runner` from 0.4.1 to 0.8.1 - [Release notes](https://github.com/nicolo-ribaudo/jest-light-runner/releases) - [Commits](https://github.com/nicolo-ribaudo/jest-light-runner/compare/v0.4.1...v0.8.1) Updates `knip` from 6.16.1 to 6.27.0 - [Release notes](https://github.com/webpro-nl/knip/releases) - [Commits](https://github.com/webpro-nl/knip/commits/knip@6.27.0/packages/knip) Updates `ts-node` from 10.9.1 to 10.9.2 - [Release notes](https://github.com/TypeStrong/ts-node/releases) - [Changelog](https://github.com/TypeStrong/ts-node/blob/main/development-docs/release-template.md) - [Commits](https://github.com/TypeStrong/ts-node/compare/v10.9.1...v10.9.2) Updates `typescript` from 5.5.2 to 5.9.3 - [Release notes](https://github.com/microsoft/TypeScript/releases) - [Commits](https://github.com/microsoft/TypeScript/compare/v5.5.2...v5.9.3) Updates `typescript-eslint` from 8.57.2 to 8.65.0 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.65.0/packages/typescript-eslint) --- updated-dependencies: - dependency-name: "@eslint/compat" dependency-version: 2.1.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: server-dev - dependency-name: "@eslint/eslintrc" dependency-version: 3.3.6 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: server-dev - dependency-name: "@eslint/js" dependency-version: 9.39.5 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: server-dev - dependency-name: "@playwright/test" dependency-version: 1.61.1 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: server-dev - dependency-name: "@types/cookie-parser" dependency-version: 1.4.10 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: server-dev - dependency-name: "@types/debug" dependency-version: 4.1.13 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: server-dev - dependency-name: "@types/express-serve-static-core" dependency-version: 5.1.2 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: server-dev - dependency-name: "@types/js-yaml" dependency-version: 4.0.9 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: server-dev - dependency-name: "@types/lodash" dependency-version: 4.17.24 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: server-dev - dependency-name: "@types/morgan" dependency-version: 1.9.10 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: server-dev - dependency-name: "@types/node" dependency-version: 18.19.130 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: server-dev - dependency-name: "@types/validator" dependency-version: 13.15.10 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: server-dev - dependency-name: "@typescript-eslint/eslint-plugin" dependency-version: 8.64.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: server-dev - dependency-name: "@typescript-eslint/parser" dependency-version: 8.65.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: server-dev - dependency-name: eslint dependency-version: 9.39.5 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: server-dev - dependency-name: eslint-import-resolver-typescript dependency-version: 3.10.1 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: server-dev - dependency-name: eslint-plugin-functional dependency-version: 9.0.5 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: server-dev - dependency-name: eslint-plugin-jsdoc dependency-version: 62.9.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: server-dev - dependency-name: eslint-plugin-promise dependency-version: 7.3.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: server-dev - dependency-name: eslint-plugin-switch-statement dependency-version: 0.0.12 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: server-dev - dependency-name: jest-light-runner dependency-version: 0.8.1 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: server-dev - dependency-name: knip dependency-version: 6.27.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: server-dev - dependency-name: ts-node dependency-version: 10.9.2 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: server-dev - dependency-name: typescript dependency-version: 5.9.3 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: server-dev - dependency-name: typescript-eslint dependency-version: 8.64.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: server-dev ... Signed-off-by: dependabot[bot] * Fix server checks after dependency updates Co-Authored-By: Codex --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Tao Bojlén Co-authored-by: Codex --- server/package-lock.json | 2001 +++++++++++++++++++++++--------------- server/package.json | 4 +- 2 files changed, 1209 insertions(+), 796 deletions(-) diff --git a/server/package-lock.json b/server/package-lock.json index 687ac8b2..22a61c2f 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -111,7 +111,7 @@ "eslint-plugin-node": "^11.1.0", "eslint-plugin-promise": "^7.2.1", "eslint-plugin-security": "^1.7.1", - "eslint-plugin-switch-statement": "^0.0.11", + "eslint-plugin-switch-statement": "^0.0.12", "jest": "^29.3.1", "jest-junit": "^16.0.0", "jest-light-runner": "^0.4.1", @@ -121,7 +121,7 @@ "supertest": "^6.2.2", "ts-node": "^10.9.1", "tsc-watch": "^4.6.0", - "typescript": "^5.5.2", + "typescript": "5.5.2", "typescript-eslint": "^8.57.2" } }, @@ -478,7 +478,8 @@ "version": "0.10.1", "resolved": "https://registry.npmjs.org/@assemblyscript/loader/-/loader-0.10.1.tgz", "integrity": "sha512-H71nDOOL8Y7kWRLqf6Sums+01Q5msqBW2KhDUTemh1tvY04eSkSXrK0uj/4mmY0Xr16/3zyZmsrxN7CKuRbNRg==", - "dev": true + "dev": true, + "license": "Apache-2.0" }, "node_modules/@aws-crypto/crc32": { "version": "5.2.0", @@ -1736,10 +1737,11 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", + "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -1840,6 +1842,38 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.29.7.tgz", + "integrity": "sha512-zGYcYfq/WmZ4V+kBIXQon9dSSc8ircGZqw9ZaNhhGj9nZkeBu1jHLBDQqYYi5WA9uawvA2sIMbry2nCFhf5Djg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-syntax-import-meta": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", @@ -1865,12 +1899,13 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz", - "integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.29.7.tgz", + "integrity": "sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1951,6 +1986,22 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-syntax-top-level-await": { "version": "7.14.5", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", @@ -1967,12 +2018,13 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", - "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.29.7.tgz", + "integrity": "sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -2125,26 +2177,26 @@ } }, "node_modules/@es-joy/jsdoccomment": { - "version": "0.84.0", - "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.84.0.tgz", - "integrity": "sha512-0xew1CxOam0gV5OMjh2KjFQZsKL2bByX1+q4j3E73MpYIdyUxcZb/xQct9ccUb+ve5KGUYbCUxyPnYB7RbuP+w==", + "version": "0.86.0", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.86.0.tgz", + "integrity": "sha512-ukZmRQ81WiTpDWO6D/cTBM7XbrNtutHKvAVnZN/8pldAwLoJArGOvkNyxPTBGsPjsoaQBJxlH+tE2TNA/92Qgw==", "dev": true, "license": "MIT", "dependencies": { "@types/estree": "^1.0.8", - "@typescript-eslint/types": "^8.54.0", - "comment-parser": "1.4.5", + "@typescript-eslint/types": "^8.58.0", + "comment-parser": "1.4.6", "esquery": "^1.7.0", - "jsdoc-type-pratt-parser": "~7.1.1" + "jsdoc-type-pratt-parser": "~7.2.0" }, "engines": { "node": "^20.19.0 || ^22.13.0 || >=24" } }, "node_modules/@es-joy/jsdoccomment/node_modules/@typescript-eslint/types": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.55.0.tgz", - "integrity": "sha512-ujT0Je8GI5BJWi+/mMoR0wxwVEQaxM+pi30xuMiJETlX80OPovb2p9E8ss87gnSVtYXtJoU9U1Cowcr6w2FE0w==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.65.0.tgz", + "integrity": "sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg==", "dev": true, "license": "MIT", "engines": { @@ -2195,13 +2247,13 @@ } }, "node_modules/@eslint/compat": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@eslint/compat/-/compat-2.0.3.tgz", - "integrity": "sha512-SjIJhGigp8hmd1YGIBwh7Ovri7Kisl42GYFjrOyHhtfYGGoLW6teYi/5p8W50KSsawUPpuLOSmsq1bD0NGQLBw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@eslint/compat/-/compat-2.1.0.tgz", + "integrity": "sha512-LgaSCymEpw7tF53xvDw9SNsraPb1IBHxpdABIOM0hW8UAlP8znrjYtuxfR58FSJ3L9BhwD+FaPRFQpZq84Nh6g==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^1.1.1" + "@eslint/core": "^1.2.1" }, "engines": { "node": "^20.19.0 || ^22.13.0 || >=24" @@ -2216,9 +2268,9 @@ } }, "node_modules/@eslint/compat/node_modules/@eslint/core": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.1.1.tgz", - "integrity": "sha512-QUPblTtE51/7/Zhfv8BDwO0qkkzQL7P/aWWbqcf4xWLEYn1oKjdO0gglQBB4GAsu7u6wjijbCmzsUTy6mnk6oQ==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", + "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -2270,9 +2322,9 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", - "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.6.tgz", + "integrity": "sha512-l2Ul9PrHsPCKcEY/ac7VgFj9D80C7S68sOKc618SyHDPK36s1XcFebXY0iTzUVn4Yq+YbwvSnDmCz9yxjX+QrA==", "dev": true, "license": "MIT", "dependencies": { @@ -2282,7 +2334,7 @@ "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", - "js-yaml": "^4.1.1", + "js-yaml": "^4.3.0", "minimatch": "^3.1.5", "strip-json-comments": "^3.1.1" }, @@ -2341,9 +2393,9 @@ "license": "MIT" }, "node_modules/@eslint/js": { - "version": "9.39.4", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", - "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", + "version": "9.39.5", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.5.tgz", + "integrity": "sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A==", "dev": true, "license": "MIT", "engines": { @@ -3502,6 +3554,16 @@ "node": ">= 8" } }, + "node_modules/@nolyfill/is-core-module": { + "version": "1.0.39", + "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz", + "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.4.0" + } + }, "node_modules/@opentelemetry/api": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.8.0.tgz", @@ -3520,9 +3582,9 @@ } }, "node_modules/@oxc-parser/binding-android-arm-eabi": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm-eabi/-/binding-android-arm-eabi-0.133.0.tgz", - "integrity": "sha512-l/44caGse+VpnY9gx0yvvc5QnnG3yG1FO3KZgYvNL1GZrfK86zIwAOgGEVlxDyRymzrU/KHiblPFpevKOmJmUA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm-eabi/-/binding-android-arm-eabi-0.137.0.tgz", + "integrity": "sha512-KDs+0VPdEmasOkpuJHW9V5WCF+cvYdMQv2Jd+aJXt+cxIx12NToRQRbXaRwUEDsZw+/jMk81Ve8ZFbjUkJTOwA==", "cpu": [ "arm" ], @@ -3537,9 +3599,9 @@ } }, "node_modules/@oxc-parser/binding-android-arm64": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm64/-/binding-android-arm64-0.133.0.tgz", - "integrity": "sha512-KUHmPMziLBp4u+zbrLdB7iWS7KshuZe+RAp7ELnY9SI9nNXBZ+dp8fiBqWOxhXqn+FQg3a4UcQhwmsJOKV8Jjg==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm64/-/binding-android-arm64-0.137.0.tgz", + "integrity": "sha512-WhALNzfy3x/RfC6bsqX+csavuUY0yHHE7XfgPE5M542uhoBZUUoGTPG+nkMbGoG4+gcfss5s7urMyn5QBHu0sw==", "cpu": [ "arm64" ], @@ -3554,9 +3616,9 @@ } }, "node_modules/@oxc-parser/binding-darwin-arm64": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.133.0.tgz", - "integrity": "sha512-q8dWmnU/8ea2tga9w2f1PinQ5rcMPDUGkF64T189b65YMjUomET4oy5oRldOr4AwOQkneOG/Zttnz1Dvrc62wg==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.137.0.tgz", + "integrity": "sha512-bFPr5hgmNMOMoyPTGtdsK4Ug21RovIPojRMgDDhSp1LtCnc/DkLwGONKjgRjszg677RlGnkYSviQ8hHaUPOVYA==", "cpu": [ "arm64" ], @@ -3571,9 +3633,9 @@ } }, "node_modules/@oxc-parser/binding-darwin-x64": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.133.0.tgz", - "integrity": "sha512-cOKeIELIB2bJnCKwqx4Rdj+1Lss/U6uCbLxRySZrhyOOQa1flKhwZFjEHRHxk8fU1NKmhK5OnTdPQ4CpjuFuVw==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.137.0.tgz", + "integrity": "sha512-CL5dMm1asqXIDZHg14FLxj3Mc36w8PI7xCWh1uA4is6z8g2XrIILoTcQYOxDbwzuk34RDPX5IAGUxZr6LA9KAg==", "cpu": [ "x64" ], @@ -3588,9 +3650,9 @@ } }, "node_modules/@oxc-parser/binding-freebsd-x64": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.133.0.tgz", - "integrity": "sha512-OpaSv4pW3KgFrMYQxTaS0aOE4T1DQF3qZE/4B6uqqv1KgPWWd4UQhJALi8PJPX1RRV5K7ThKXRfF7qGg2+3l1A==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.137.0.tgz", + "integrity": "sha512-79h8rYGnSlKPGWo7mHr2ixO6ea7aW8B0CT965SZ8SLbNnCOH5aOYBTeVXUY6eMvEaiLyWr8Skuiugr5pDYgLGw==", "cpu": [ "x64" ], @@ -3605,9 +3667,9 @@ } }, "node_modules/@oxc-parser/binding-linux-arm-gnueabihf": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.133.0.tgz", - "integrity": "sha512-JGK1wlGrGwxBIlVSF7KWTX1/ru6BEtf28fRROztDRkLfiW+Kxa4onnriezMIiogfn9hVw2KzYcKiLjkLR2ns8A==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.137.0.tgz", + "integrity": "sha512-ASgmlSimhGyr0lksgVIo6hibz1obnDq4qJbiMX/AzltfgPnanRrzG1Q+23g8ljOHOjv6dsznkUuCYL3gg0sY1Q==", "cpu": [ "arm" ], @@ -3622,9 +3684,9 @@ } }, "node_modules/@oxc-parser/binding-linux-arm-musleabihf": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.133.0.tgz", - "integrity": "sha512-yuZO533Ftonxn/iyoqQzURzLQHMspvsIyfiCSNi1t/ER4eIQaR0SsmUOUm5b/lmSig7IWIUa5/BrbEkAPwcilQ==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.137.0.tgz", + "integrity": "sha512-AU2J9aa22Sx32wRGnDjybOU9TQXXQUud5sdUi+ZB0XxwM8aToWLweV+yA0wlQm0yIUVqljquqoHCYEq9II8gJQ==", "cpu": [ "arm" ], @@ -3639,13 +3701,16 @@ } }, "node_modules/@oxc-parser/binding-linux-arm64-gnu": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.133.0.tgz", - "integrity": "sha512-hvpbqT5pN2rR+3+xtWeizwfR/aZ0vGceg6TqYMl+ToxMpk9/tmnX7kSvQnfEUkoua8mhogzvIKsAkn0wxgblBA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.137.0.tgz", + "integrity": "sha512-GdEtiG89yMr7XkUGxifgodXEEm2f+xW2f9CpDjlgAnBOwhTmrpQMvhOGobLVKUyzf/qHBXW16smk5zbF3nZU6w==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -3656,13 +3721,16 @@ } }, "node_modules/@oxc-parser/binding-linux-arm64-musl": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.133.0.tgz", - "integrity": "sha512-wJQGamIosQBoJHW9+S5XxrtKRo3eyJxsnS1XCPrqN0LHi8uw1pTqqTfn3t/NVuvbBg7Pumn4ez9Eidgcn0xbEg==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.137.0.tgz", + "integrity": "sha512-EGJ+Bs8iXx8KBH8DQ5BLoEm5lnHaYjlh4/8j8vFhrr/6z4tqONy5BZDzLpKmmNWlN6Hlc5r8YOuBVHqZ9vRFEQ==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -3673,13 +3741,16 @@ } }, "node_modules/@oxc-parser/binding-linux-ppc64-gnu": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.133.0.tgz", - "integrity": "sha512-Koaz32/O5+abIfrNGdyndgRvdOZ9jEf5/z3Ep9h3h2QWpdDiUQpVwgH0OcMXCs+l9aXxPLtkupqyVig9W6FDKw==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.137.0.tgz", + "integrity": "sha512-vzFUQENy/fnbSe5DZWovq6tIBc1uhuMztanSW6rz1e9WdQE4gHwYuD7ZII6JnrJifd1R3RSoqiZbgRFlVL2tYQ==", "cpu": [ "ppc64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -3690,13 +3761,16 @@ } }, "node_modules/@oxc-parser/binding-linux-riscv64-gnu": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.133.0.tgz", - "integrity": "sha512-R4vOjWzxhnNWHnVLeiB6jNuIifdy9vcMXZGPc7StXcxBovI+U2zg1QhZ9o8OjV80oGivs1lX5NfPLzk4IPqlRA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.137.0.tgz", + "integrity": "sha512-SfVI14HBQs9gtLcUD5hTt5hsNbdrqSUNg9S8muN+LhVQ5nf1WwH3hAoK6B9NKgdYgWAQSXFXGiiBedQ4r/BKuw==", "cpu": [ "riscv64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -3707,13 +3781,16 @@ } }, "node_modules/@oxc-parser/binding-linux-riscv64-musl": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.133.0.tgz", - "integrity": "sha512-iwgBNUTHiMdxARLYuM0SBlnYeb19iw1Ea5M+4ERZupCsBMLArti6FyZ6UfFjJxIiTDr2oW2DGQFxlQVQ/dW9rA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.137.0.tgz", + "integrity": "sha512-e7Ppy4FCIFNQxT/ikSeIWFoQ0l+N9vgtRBtLcyZXeolTzApyVoPqEXsYPrcdM/9i0Bwk8knvYd37vaEMxHyi6g==", "cpu": [ "riscv64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -3724,13 +3801,16 @@ } }, "node_modules/@oxc-parser/binding-linux-s390x-gnu": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.133.0.tgz", - "integrity": "sha512-ZwZNo8FZmB/gVfboQl+wXilBigGl+6nQQs+nITOeAP/HcAOjiHl6XZJL9F/KXNEspODQcbjAiyjUbeCJd9a0fA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.137.0.tgz", + "integrity": "sha512-Bho5qFwdhqsIFR7gipYEUlqvi3SRrY8sugxXig380MIaakBB1PyU9+7dBiBVScfImTNWhijUxdBwqrprGdq5WA==", "cpu": [ "s390x" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -3741,13 +3821,16 @@ } }, "node_modules/@oxc-parser/binding-linux-x64-gnu": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.133.0.tgz", - "integrity": "sha512-govCvWx1dBlED3uu4qXctxpRcouu9I8Kn+DBktGCl760JtlGJzc9l/OmPJKlYWSbrRqKkMZehNeZ/4Wfma7uSA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.137.0.tgz", + "integrity": "sha512-36mGWtg7PyFzjJwGDkH6/F4o2nIDEoKXLPr/X/lwqklkomQwJJt1I5GJVmGhovUEmgPK5WAeAZMqlFCehwiy9Q==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -3758,13 +3841,16 @@ } }, "node_modules/@oxc-parser/binding-linux-x64-musl": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.133.0.tgz", - "integrity": "sha512-ssTlpXD5Mq9uCssDJPzlRWqBt4Y7Zzd9i+XZhWmK/9Y6KUIuAxVYTYiI8lxcGWi0+3/Cz4A8q9UrD4NK9Y2j7g==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.137.0.tgz", + "integrity": "sha512-/Jqx6+N7A44n2BdvUr7pXhVr2vFjs6WGH3unZRczwrfiH0H1zY0QwKQMG/dtRiTlKGDKGukznPT8lx84/oEsZg==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -3775,9 +3861,9 @@ } }, "node_modules/@oxc-parser/binding-openharmony-arm64": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-openharmony-arm64/-/binding-openharmony-arm64-0.133.0.tgz", - "integrity": "sha512-51aByfXhPtLEdWG4a2Ihdw6cPWV1ei1AarALpFdDP8MLWDLE2NuUMgbo3DERR2Kt8fT/ok1GUvBiLxVGke9uUQ==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-openharmony-arm64/-/binding-openharmony-arm64-0.137.0.tgz", + "integrity": "sha512-9Uj0qHNNl+OgT1UTGwF7ixIXU6T1u2SbMidmgPy/h1h/fl2gRS6YpAxxY1gwHofcWjoTwkoMFd8xs5Vuj6GOFA==", "cpu": [ "arm64" ], @@ -3792,9 +3878,9 @@ } }, "node_modules/@oxc-parser/binding-wasm32-wasi": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-wasm32-wasi/-/binding-wasm32-wasi-0.133.0.tgz", - "integrity": "sha512-2e16tkKp+wDO2GTAmXfxbBcCmGEaFPIJEIRBBmVKNVXSc8/fJsSIaBGyFTPHM9ST5GNWgJcYIt94rDTks+PLwA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-wasm32-wasi/-/binding-wasm32-wasi-0.137.0.tgz", + "integrity": "sha512-gW2vfkytNGgMVADiuzdvOfw0mWG9za20F/1fCJsif5aBMAvWJTSbpIXbIe0XkOe0VENk+PadpQ7cZgUy2sUJcA==", "cpu": [ "wasm32" ], @@ -3802,18 +3888,74 @@ "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "1.10.0", - "@emnapi/runtime": "1.10.0", - "@napi-rs/wasm-runtime": "^1.1.4" + "@emnapi/core": "1.11.1", + "@emnapi/runtime": "1.11.1", + "@napi-rs/wasm-runtime": "^1.1.5" }, "engines": { "node": "^20.19.0 || >=22.12.0" } }, + "node_modules/@oxc-parser/binding-wasm32-wasi/node_modules/@emnapi/core": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", + "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@oxc-parser/binding-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", + "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@oxc-parser/binding-wasm32-wasi/node_modules/@emnapi/wasi-threads": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", + "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@oxc-parser/binding-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.0.tgz", + "integrity": "sha512-kDoONqMa+VnZ4vvvu/ZUurpJ4gkZU57e7g69qpNgWhYcZFPUHZM2CEMKm+cG6ufDVALbjMvfmMjFVqaK7uEMnA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.3" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=23.5.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^2.0.0-alpha.3", + "@emnapi/runtime": "^2.0.0-alpha.3" + } + }, "node_modules/@oxc-parser/binding-win32-arm64-msvc": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.133.0.tgz", - "integrity": "sha512-KPTNDKbxH1cglrqTyVeXHb4Pk4oksz8EcE1/v8zqU7N4UXbiHfA/IwtXZ2U77fnRAWBbgVkl/lZbL7o3hRdejg==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.137.0.tgz", + "integrity": "sha512-x+pFANF0yL5uK/6T7lu6SlR5qid6sp//eZXKLq5iNsIE+EQg6EaS8/wsW7E91nXXjpnPhSoMOHXShSVhGRdn8w==", "cpu": [ "arm64" ], @@ -3828,9 +3970,9 @@ } }, "node_modules/@oxc-parser/binding-win32-ia32-msvc": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.133.0.tgz", - "integrity": "sha512-Una1bNYv9zCavQrfnDR9wuZVB3itLjCEH4Oz7i6CwAJN/Xq9b+zbbcxmvdkKvvJt4Ngc/MBmIYlbLo3zS4TQ0A==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.137.0.tgz", + "integrity": "sha512-sQUqym80PFi6McRsIqfJrSu2JrSClEZIXXD+/FjAFoULEKzOPsldIdFBG96xdX8aVMzCNQ9792FPx3MfkEIrFA==", "cpu": [ "ia32" ], @@ -3845,9 +3987,9 @@ } }, "node_modules/@oxc-parser/binding-win32-x64-msvc": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.133.0.tgz", - "integrity": "sha512-kjBhCiOGSYTwDJQuuZa7a94JbP8htWu7J0X1KwH74kV2K5eYf6eyJRYmkpCDvr0XEL8tMxYI4WU1VekblFCLgg==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.137.0.tgz", + "integrity": "sha512-2AsevxlvNN4WKxpEn3RtqD5zbqMaXF+T7JXblsP4gVuY+vC9dXS4ED/PwfRCliFqoeisYS3Iro4DHzxr0TEvVA==", "cpu": [ "x64" ], @@ -3862,9 +4004,9 @@ } }, "node_modules/@oxc-project/types": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz", - "integrity": "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.137.0.tgz", + "integrity": "sha512-WT+Gb24i8hmvo85AIv2oEYouEXkRlKAlT9WaCa3TfLgNCN+GhrJOGZuIlMouAh38Qe4QOx26eUOVsq70qXrywA==", "dev": true, "license": "MIT", "funding": { @@ -3872,9 +4014,9 @@ } }, "node_modules/@oxc-resolver/binding-android-arm-eabi": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm-eabi/-/binding-android-arm-eabi-11.20.0.tgz", - "integrity": "sha512-IjfWOXRgJFNdORDl+Uf1aibNgZY2guOD3zmOhx1BGVb/MIiqlFTdmjpQNplSN58lhWehnX4UNqC3QwpUo8pjJg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm-eabi/-/binding-android-arm-eabi-11.21.3.tgz", + "integrity": "sha512-eNU11A2WNizh04v3uyaJCootrHIaS0B9aHYXvAvVnPNk4xYSjMUjHnhQ6dewPN2MRYDskV85d1N0Aw0WNWhcyg==", "cpu": [ "arm" ], @@ -3886,9 +4028,9 @@ ] }, "node_modules/@oxc-resolver/binding-android-arm64": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm64/-/binding-android-arm64-11.20.0.tgz", - "integrity": "sha512-QqslZAuFQG8Q9xm7JuIn8JUbvywhSBMVhuQHtYW+auirZJloS41oxUUaBXk7uUhZJgp44c5zQLeVvmFaDQB+2Q==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm64/-/binding-android-arm64-11.21.3.tgz", + "integrity": "sha512-8Q+ZjTLvn2dIcWsrmhdrEihm7q+ag/k+mkry7Z+t0QbbHaVxXQfvH9AewyVMh/WrpEKhQ3DDgx9fYbqeCpeOEw==", "cpu": [ "arm64" ], @@ -3900,9 +4042,9 @@ ] }, "node_modules/@oxc-resolver/binding-darwin-arm64": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-arm64/-/binding-darwin-arm64-11.20.0.tgz", - "integrity": "sha512-MUcavykj2ewlR+kc5arpg4tC2RvzJkUxWtNv74pf7lcNk00GpIpN43vXMj+j6r4eMmfZhlb8hueKoIb8e9kAGQ==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-arm64/-/binding-darwin-arm64-11.21.3.tgz", + "integrity": "sha512-wkh0qKZGHXVUDxFw3oA1TXnU2BDYY/r775oJflGeIr8uDPPoN2pk8gijQIzYRT6hoql/lg3+Tx/SaTn9e2/aGg==", "cpu": [ "arm64" ], @@ -3914,9 +4056,9 @@ ] }, "node_modules/@oxc-resolver/binding-darwin-x64": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-x64/-/binding-darwin-x64-11.20.0.tgz", - "integrity": "sha512-BGB16nRUK5Etiv//ihPyzj8Lj1px0mhh4YIfe0FDf045ywknfSm0GEbiRESpr6Q4K82AvnyaRIhhluHByvS4bg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-x64/-/binding-darwin-x64-11.21.3.tgz", + "integrity": "sha512-HbNc23FAQYbuyDV2vBWMez4u4mrsm5RAkniGZAWqr6lYZ3N4beeqIb776jzwRl8qL2zRhHVXpUj97X0QgogVzg==", "cpu": [ "x64" ], @@ -3928,9 +4070,9 @@ ] }, "node_modules/@oxc-resolver/binding-freebsd-x64": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-freebsd-x64/-/binding-freebsd-x64-11.20.0.tgz", - "integrity": "sha512-JZgtePaqj3qmD5XFHJaSLWzHRxQu0LaPkdoM1KJXYADvAaa83ijXHclV3ej3CueeW0wxfIAbGCZVP45J0CA7uQ==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-freebsd-x64/-/binding-freebsd-x64-11.21.3.tgz", + "integrity": "sha512-K6xNsTUPEUdfrn0+kbMq5nOUB5w1C5pavPQngt4TM2FpN91lP0PBe2srSpamb4d69O7h86oAi/qWX/kZNRSjkw==", "cpu": [ "x64" ], @@ -3942,9 +4084,9 @@ ] }, "node_modules/@oxc-resolver/binding-linux-arm-gnueabihf": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-11.20.0.tgz", - "integrity": "sha512-hOQ/p3ry3v3SchUBXicrrnszaI/UmYzM4wtS4RGfwgVUX7a+HbyQSzJ5aOzu+o6XZkFkS3ZXN4PZAzhOb77OSg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-11.21.3.tgz", + "integrity": "sha512-VcFmOpcpWX1zoEy8M58tR2M9YxM+Z9RuQhqAx5q0CTmrruaP7Gveejg75hzd/5sg5nk9G3aLALEa3hE2FsmmTQ==", "cpu": [ "arm" ], @@ -3956,9 +4098,9 @@ ] }, "node_modules/@oxc-resolver/binding-linux-arm-musleabihf": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-11.20.0.tgz", - "integrity": "sha512-2ArPksaw0AqeuGBfoS715VF+JvJQAhD2niWgjE5hVO+L+nAfikVQopvngCMX9x4BD8itWoQ3dnikrQyl5Ho5Jg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-11.21.3.tgz", + "integrity": "sha512-quVoxFLBy43hWaQbbDtQNRwAX5vX76mv7n64icAtQcJ3eNgVeblqmkupF/hAneNthdqSlnd1sTjb3aQSaDPaCQ==", "cpu": [ "arm" ], @@ -3970,13 +4112,16 @@ ] }, "node_modules/@oxc-resolver/binding-linux-arm64-gnu": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-11.20.0.tgz", - "integrity": "sha512-0bJnmYFp62JdZ4nVMDUZ/C58BCZOCcqgKtnUlp7L9Ojf/czIN+3j72YlLPeWLkzlr6SlYvIQA4SGV/HyO0d+qg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-11.21.3.tgz", + "integrity": "sha512-X0AqNZgcD07Q4V3RDK18/vYOj/HQT/FnmEFGYS2jTWqY7JO13ryE3TEs3eAIgUJhBnNkpEaiXqz3VK8M7qQhWQ==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -3984,13 +4129,16 @@ ] }, "node_modules/@oxc-resolver/binding-linux-arm64-musl": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-musl/-/binding-linux-arm64-musl-11.20.0.tgz", - "integrity": "sha512-wKHHzPKZo7Ufhv/Bt6yxT7FOgnIgW4gwXcJUipkShGp68W3wGVqvr1Sr0fY65lN0Oy6y41+g2kIDvkgZaMMUkw==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-musl/-/binding-linux-arm64-musl-11.21.3.tgz", + "integrity": "sha512-YkaQnaKYdbuaXvRt5Qd0GpbihzVnyfR6z1SpYfIUC6RTu4NF7lDKPjVkYb+jRI2gedVO2rVpN35Y6akG6ud4Lw==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -3998,13 +4146,16 @@ ] }, "node_modules/@oxc-resolver/binding-linux-ppc64-gnu": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-11.20.0.tgz", - "integrity": "sha512-RN8goF7Ie0B79L4i4G6OeBocTgSC56vJbQ65VJje+oXnldVpLnOU7j/AQ/dP94TcCS+Yh6WG8u3Qt4ETteXFNQ==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-11.21.3.tgz", + "integrity": "sha512-gB9HwhrPiFqUzDeEq+y/CgAijz1YdI6BnXz5GaH2Pa9cWdutchlkGFAiAuGb/PjVQpiK6NFKzFuztxrweoit7A==", "cpu": [ "ppc64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -4012,13 +4163,16 @@ ] }, "node_modules/@oxc-resolver/binding-linux-riscv64-gnu": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-11.20.0.tgz", - "integrity": "sha512-5l1yU6/xQEqLZRzxqmMxJfWPslpwCmBsdDGaBvABPehxquCXDC7dd7oraNdKSJUMDXSM7VvVj8H2D2FTjU7oWw==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-11.21.3.tgz", + "integrity": "sha512-zjDWBlYk8QGv0H8dsPUWqkfjYIIjG2TvspGkzXL0eImbgxtZorA/klKeHyolevoT3Kvbi+1iMr9Lhrh7jf54Og==", "cpu": [ "riscv64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -4026,13 +4180,16 @@ ] }, "node_modules/@oxc-resolver/binding-linux-riscv64-musl": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-11.20.0.tgz", - "integrity": "sha512-xHEvkbgz6UC+A3JOyDQy76LkUaxsNSfIr3/GV8slwZsnuooJiIB34gzJfsyvR4JdCYNUUPsRJc/w/oWkODu+hg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-11.21.3.tgz", + "integrity": "sha512-4UfsQvacV388y1zpXL7C1x1FNYaV52JtuNRiuzrfQA2z1z6ElVrsidkGsrvQ5EgeSq1Pj7kaKqrgGkvFuxJ/tw==", "cpu": [ "riscv64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -4040,13 +4197,16 @@ ] }, "node_modules/@oxc-resolver/binding-linux-s390x-gnu": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-11.20.0.tgz", - "integrity": "sha512-aWPDUUmSeyHvlW+SoEUd+JIJsQhVhu6a5tBpDRMu058naPAchTgAVGCFy35zjbnFlt0i8hLWziff6HX0D3LU4g==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-11.21.3.tgz", + "integrity": "sha512-b5uH+HKH0MP5mNBYaK75SKsJbw52URqrx2LavYdq6wb0l3ExAG5niYRP9DWUNHdKilpaBVM2bXk9HNWrH3ew7Q==", "cpu": [ "s390x" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -4054,13 +4214,16 @@ ] }, "node_modules/@oxc-resolver/binding-linux-x64-gnu": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-gnu/-/binding-linux-x64-gnu-11.20.0.tgz", - "integrity": "sha512-x2YeSimvhJjKLVD8KSu8f/rqU1potcdEMkApIPJqjZWN7c2Fpt4g2X32WDg1p+XDAmyT7nuQGe0vnhvXeLbH+g==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-gnu/-/binding-linux-x64-gnu-11.21.3.tgz", + "integrity": "sha512-PjYlmilBpNRh2ntXNYAK3Am5w/nPfEpnU/96iNx7CI8EzAn12J4JRiec63wHJTH31nLoCNxBg/829pN+3CfG3Q==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -4068,13 +4231,16 @@ ] }, "node_modules/@oxc-resolver/binding-linux-x64-musl": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-musl/-/binding-linux-x64-musl-11.20.0.tgz", - "integrity": "sha512-kcRLEIxpZefeYfLChjpgFf3ilBzRDZ+yobMrpRsQlSrxuFGtm3U6PMU7AaEpMqo3NfDGVyJJseAjnRLzMFHjwQ==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-musl/-/binding-linux-x64-musl-11.21.3.tgz", + "integrity": "sha512-QTBAb7JuHlZ7JUEyM8UiQi2f7m/L4swBhP2TNpYIDc9Wp/wRw1G/8sl6i13aIzQAXH7LKIm294LeOHd0lQR8zA==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -4082,9 +4248,9 @@ ] }, "node_modules/@oxc-resolver/binding-openharmony-arm64": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-openharmony-arm64/-/binding-openharmony-arm64-11.20.0.tgz", - "integrity": "sha512-HHcfnApSZGtKhTiHqe8OZruOZe5XuFQH5/E0Yhj3u8fnFvzkM4/k6WjacUf4SvA0SPEAbfbgYmVPuo0VX/fIBQ==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-openharmony-arm64/-/binding-openharmony-arm64-11.21.3.tgz", + "integrity": "sha512-4j1DFwjwv36ec9kds0jU/ucQ5Ha4ERO/H95BxR5JFf0kqUUAJ1kwII7XhTc1vZrkdJkvLGC9Q2MbpObpum8RBg==", "cpu": [ "arm64" ], @@ -4096,9 +4262,9 @@ ] }, "node_modules/@oxc-resolver/binding-wasm32-wasi": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-wasm32-wasi/-/binding-wasm32-wasi-11.20.0.tgz", - "integrity": "sha512-Tn0y1XOFYHNfK1wp1Z5QK8Rcld/bsOwRISQXfqAZ5IBpv8Gz1IvV39fUWNprqNdRizgcvFhOzWwFun2zkJsyBg==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-wasm32-wasi/-/binding-wasm32-wasi-11.21.3.tgz", + "integrity": "sha512-i8oluoel5kru/j1WNrjmQSiA3GQ7wvIYVR1IwIoZtKogAhya2iub+ZKIeSIkcJOrnzQ18Tzl/F+kL3fYOxZLvA==", "cpu": [ "wasm32" ], @@ -4106,18 +4272,74 @@ "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "1.10.0", - "@emnapi/runtime": "1.10.0", - "@napi-rs/wasm-runtime": "^1.1.4" + "@emnapi/core": "1.11.0", + "@emnapi/runtime": "1.11.0", + "@napi-rs/wasm-runtime": "^1.1.5" }, "engines": { "node": ">=14.0.0" } }, + "node_modules/@oxc-resolver/binding-wasm32-wasi/node_modules/@emnapi/core": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.0.tgz", + "integrity": "sha512-l9Oo58x0HOP5znGzVhYW9U3e5wVuA4LAZU2AGezTmkhO1CgQRFDhDg4nneHsu/t3WniXg9QrG2nIXL/ZS8ln8Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@oxc-resolver/binding-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.0.tgz", + "integrity": "sha512-55coeOFKHv1ywEcUXJtWU5f+Jr/W5tZDvZig8DLKSwUN1JpROQ4rk/SNOQiFWmaR/VKF4zuFyW1B8JduOSv6Pg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@oxc-resolver/binding-wasm32-wasi/node_modules/@emnapi/wasi-threads": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", + "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@oxc-resolver/binding-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.0.tgz", + "integrity": "sha512-kDoONqMa+VnZ4vvvu/ZUurpJ4gkZU57e7g69qpNgWhYcZFPUHZM2CEMKm+cG6ufDVALbjMvfmMjFVqaK7uEMnA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.3" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=23.5.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^2.0.0-alpha.3", + "@emnapi/runtime": "^2.0.0-alpha.3" + } + }, "node_modules/@oxc-resolver/binding-win32-arm64-msvc": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-11.20.0.tgz", - "integrity": "sha512-qPi25YNPe4YenS8MgsQU2+bIFHxxpLx1LVna2444cEHqNPhNjvWf9zqj4aWE43H9LpAsTmkkAlA3eL5ElBU3mA==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-11.21.3.tgz", + "integrity": "sha512-M/8dw8dD6aOs+NlPJax401CZB9I7Aut84isQLgALGGwke4Afvw+/7yYhZb94yXf6t2sPLhQLmSmtSV+2FhsOWg==", "cpu": [ "arm64" ], @@ -4129,9 +4351,9 @@ ] }, "node_modules/@oxc-resolver/binding-win32-x64-msvc": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-x64-msvc/-/binding-win32-x64-msvc-11.20.0.tgz", - "integrity": "sha512-Wb14jWEW8huH6It9F6sXd9vrYmIS7pMrgkU6sxpLxkP+9z+wRgs71hUEhRpcn8FOXAFa27FVWfY2tRpbfTzfLw==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-x64-msvc/-/binding-win32-x64-msvc-11.21.3.tgz", + "integrity": "sha512-H7BCt/VnS9hnmMp42eGhZ99izSCRvlnWwy/N71K1/J8QoExwY4262Z8QiEkMDtduRJrztayDxETTckmUuAVL9Q==", "cpu": [ "x64" ], @@ -4163,13 +4385,13 @@ } }, "node_modules/@playwright/test": { - "version": "1.61.0", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.61.0.tgz", - "integrity": "sha512-cKA5B6lpFEMyMGjxF54QihfYpB4FkEGH+qZhtArDEG+wezQAJY8Pq6C7T1SjWz+FFzt3TbyoXBQYk/0292TdJA==", + "version": "1.61.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.61.1.tgz", + "integrity": "sha512-8nKv6+0RJSL9FE4jYOEGXnPeM/Hg12qZpmqzZjRh3qM0Y7c3z1mrOTfFLids72RDQYVh9WpLEfR5WdpNX4fkig==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright": "1.61.0" + "playwright": "1.61.1" }, "bin": { "playwright": "cli.js" @@ -11323,9 +11545,9 @@ "dev": true }, "node_modules/@tybys/wasm-util": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", - "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", "dev": true, "license": "MIT", "optional": true, @@ -11413,11 +11635,12 @@ } }, "node_modules/@types/cookie-parser": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.3.tgz", - "integrity": "sha512-CqSKwFwefj4PzZ5n/iwad/bow2hTCh0FlNAeWLtQM3JA/NX/iYagIpWG2cf1bQKQ2c9gU2log5VUCrn7LDOs0w==", + "version": "1.4.10", + "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.10.tgz", + "integrity": "sha512-B4xqkqfZ8Wek+rCOeRxsjMS9OgvzebEzzLYw7NHYuvzb7IdxOkI0ZHGgeEBX4PUM7QGVvNSK60T3OvWj3YfBRg==", "dev": true, - "dependencies": { + "license": "MIT", + "peerDependencies": { "@types/express": "*" } }, @@ -11438,9 +11661,9 @@ } }, "node_modules/@types/debug": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", - "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", + "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", "license": "MIT", "dependencies": { "@types/ms": "*" @@ -11465,9 +11688,9 @@ } }, "node_modules/@types/express-serve-static-core": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.1.tgz", - "integrity": "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.2.tgz", + "integrity": "sha512-d3KvEXBSo/lOAMc2u6fkyDHBvetBHeqD7wm/AcXfLpSOQwlmG9D/aQ0SFswVjv05p7ullQS7Mjohj6/VdbZuTg==", "license": "MIT", "dependencies": { "@types/node": "*", @@ -11534,10 +11757,11 @@ "integrity": "sha512-/K3ds8TRAfBvi5vfjuz8y6+GiAYBZ0x4tXv1Av6CWBWn0IlADc+ZX9pMq7oU0fNQPnBwIZl3rmeLp6SBApbxSQ==" }, "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", - "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", - "dev": true + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" }, "node_modules/@types/istanbul-lib-report": { "version": "3.0.0", @@ -11549,10 +11773,11 @@ } }, "node_modules/@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", "dev": true, + "license": "MIT", "dependencies": { "@types/istanbul-lib-report": "*" } @@ -11568,10 +11793,11 @@ } }, "node_modules/@types/js-yaml": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.5.tgz", - "integrity": "sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==", - "dev": true + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", + "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==", + "dev": true, + "license": "MIT" }, "node_modules/@types/json-schema": { "version": "7.0.15", @@ -11599,10 +11825,11 @@ } }, "node_modules/@types/lodash": { - "version": "4.14.198", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.198.tgz", - "integrity": "sha512-trNJ/vtMZYMLhfN45uLq4ShQSw0/S7xCTLLVM+WM1rmFpba/VS42jVUgaO3w/NOLiWR/09lnYk0yMaA/atdIsg==", - "dev": true + "version": "4.17.24", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.24.tgz", + "integrity": "sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ==", + "dev": true, + "license": "MIT" }, "node_modules/@types/long": { "version": "4.0.2", @@ -11615,10 +11842,11 @@ "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==" }, "node_modules/@types/morgan": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@types/morgan/-/morgan-1.9.4.tgz", - "integrity": "sha512-cXoc4k+6+YAllH3ZHmx4hf7La1dzUk6keTR4bF4b4Sc0mZxU/zK4wO7l+ZzezXm/jkYj/qC+uYGZrarZdIVvyQ==", + "version": "1.9.10", + "resolved": "https://registry.npmjs.org/@types/morgan/-/morgan-1.9.10.tgz", + "integrity": "sha512-sS4A1zheMvsADRVfT0lYbJ4S9lmsey8Zo2F7cnbYjWHP67Q0AwMYuuzLlkIM2N8gAbb9cubhIVFwcIN2XyYCkA==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*" } @@ -11629,9 +11857,13 @@ "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==" }, "node_modules/@types/node": { - "version": "18.17.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.17.3.tgz", - "integrity": "sha512-2x8HWtFk0S99zqVQABU9wTpr8wPoaDHZUcAkoTKH+nL7kPv3WUI9cRi/Kk5Mz4xdqXSqTkKP7IWNoQQYCnDsTA==" + "version": "18.19.130", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.130.tgz", + "integrity": "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==", + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } }, "node_modules/@types/passport": { "version": "1.0.17", @@ -11693,10 +11925,11 @@ } }, "node_modules/@types/stack-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", - "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", - "dev": true + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true, + "license": "MIT" }, "node_modules/@types/superagent": { "version": "4.1.18", @@ -11718,10 +11951,11 @@ } }, "node_modules/@types/validator": { - "version": "13.9.0", - "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.9.0.tgz", - "integrity": "sha512-NclP0IbzHj/4tJZKFqKh8E7kZdgss+MCUYV9G+TLltFfDA4lFgE4PKPpDIyS2FlcdANIfSx273emkupvChigbw==", - "dev": true + "version": "13.15.10", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.10.tgz", + "integrity": "sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA==", + "dev": true, + "license": "MIT" }, "node_modules/@types/xml-encryption": { "version": "1.2.4", @@ -11758,17 +11992,17 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.0.tgz", - "integrity": "sha512-HyAZtpdkgZwpq8Sz3FSUvCR4c+ScbuWa9AksK2Jweub7w4M3yTz4O11AqVJzLYjy/B9ZWPyc81I+mOdJU/bDQw==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.65.0.tgz", + "integrity": "sha512-IEgob78X12rHpUmtcwFsXhZdVGJtwTVP8FiCLZkR6GlYVrl2PcuB+KhCE5BlVC/eQpQnu8WXRtkHZuPar+gCRA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.59.0", - "@typescript-eslint/type-utils": "8.59.0", - "@typescript-eslint/utils": "8.59.0", - "@typescript-eslint/visitor-keys": "8.59.0", + "@typescript-eslint/scope-manager": "8.65.0", + "@typescript-eslint/type-utils": "8.65.0", + "@typescript-eslint/utils": "8.65.0", + "@typescript-eslint/visitor-keys": "8.65.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" @@ -11781,20 +12015,20 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.59.0", + "@typescript-eslint/parser": "^8.65.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.0.tgz", - "integrity": "sha512-UzR16Ut8IpA3Mc4DbgAShlPPkVm8xXMWafXxB0BocaVRHs8ZGakAxGRskF7FId3sdk9lgGD73GSFaWmWFDE4dg==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.65.0.tgz", + "integrity": "sha512-Esbl8OSYiVxBokYgWPf7VVWg/BE798wXhimnn9ML9Pt5qoDf8bfQlgjlKXR/k98+AcNzlLKYrpCcrcuZ9DZLgg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.59.0", - "@typescript-eslint/visitor-keys": "8.59.0" + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/visitor-keys": "8.65.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -11805,9 +12039,9 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.0.tgz", - "integrity": "sha512-nLzdsT1gdOgFxxxwrlNVUBzSNBEEHJ86bblmk4QAS6stfig7rcJzWKqCyxFy3YRRHXDWEkb2NralA1nOYkkm/A==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.65.0.tgz", + "integrity": "sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg==", "dev": true, "license": "MIT", "engines": { @@ -11819,13 +12053,13 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.0.tgz", - "integrity": "sha512-/uejZt4dSere1bx12WLlPfv8GktzcaDtuJ7s42/HEZ5zGj9oxRaD4bj7qwSunXkf+pbAhFt2zjpHYUiT5lHf0Q==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.65.0.tgz", + "integrity": "sha512-8C71BQkGjiMmXtop7pHVJu1l2NNShFdkCyD6a2ezzs5vU/L3LRtb69EtcteFwz0mYMPzIgOw0n6OV4VBUWZd7A==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.59.0", + "@typescript-eslint/types": "8.65.0", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -11873,16 +12107,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.0.tgz", - "integrity": "sha512-TI1XGwKbDpo9tRW8UDIXCOeLk55qe9ZFGs8MTKU6/M08HWTw52DD/IYhfQtOEhEdPhLMT26Ka/x7p70nd3dzDg==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.65.0.tgz", + "integrity": "sha512-CZ4nMxWwgu1HEEFNkeaCptra9QCtkmKdgf3sWh1rl1trIhmxLilgTV4cwcbQ4wemnT4sWQN8CaKOmdYx+g2gMA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.59.0", - "@typescript-eslint/types": "8.59.0", - "@typescript-eslint/typescript-estree": "8.59.0", - "@typescript-eslint/visitor-keys": "8.59.0", + "@typescript-eslint/scope-manager": "8.65.0", + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/typescript-estree": "8.65.0", + "@typescript-eslint/visitor-keys": "8.65.0", "debug": "^4.4.3" }, "engines": { @@ -11897,67 +12131,28 @@ "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/project-service": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.0.tgz", - "integrity": "sha512-Lw5ITrR5s5TbC19YSvlr63ZfLaJoU6vtKTHyB0GQOpX0W7d5/Ir6vUahWi/8Sps/nOukZQ0IB3SmlxZnjaKVnw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.59.0", - "@typescript-eslint/types": "^8.59.0", - "debug": "^4.4.3" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" - } - }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.0.tgz", - "integrity": "sha512-UzR16Ut8IpA3Mc4DbgAShlPPkVm8xXMWafXxB0BocaVRHs8ZGakAxGRskF7FId3sdk9lgGD73GSFaWmWFDE4dg==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.65.0.tgz", + "integrity": "sha512-Esbl8OSYiVxBokYgWPf7VVWg/BE798wXhimnn9ML9Pt5qoDf8bfQlgjlKXR/k98+AcNzlLKYrpCcrcuZ9DZLgg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.59.0", - "@typescript-eslint/visitor-keys": "8.59.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/visitor-keys": "8.65.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.0.tgz", - "integrity": "sha512-91Sbl3s4Kb3SybliIY6muFBmHVv+pYXfybC4Oolp3dvk8BvIE3wOPc+403CWIT7mJNkfQRGtdqghzs2+Z91Tqg==", - "dev": true, - "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.0.tgz", - "integrity": "sha512-nLzdsT1gdOgFxxxwrlNVUBzSNBEEHJ86bblmk4QAS6stfig7rcJzWKqCyxFy3YRRHXDWEkb2NralA1nOYkkm/A==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.65.0.tgz", + "integrity": "sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg==", "dev": true, "license": "MIT", "engines": { @@ -11969,16 +12164,16 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.0.tgz", - "integrity": "sha512-O9Re9P1BmBLFJyikRbQpLku/QA3/AueZNO9WePLBwQrvkixTmDe8u76B6CYUAITRl/rHawggEqUGn5QIkVRLMw==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.65.0.tgz", + "integrity": "sha512-JboAE2swaYt4tb1fHhHTABE2K+OLy09XfcTbhnk4Pw96f9dd2e9iYsJ28gBggHlo5z5x1rkyWvcPoTuNTd4oGg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.59.0", - "@typescript-eslint/tsconfig-utils": "8.59.0", - "@typescript-eslint/types": "8.59.0", - "@typescript-eslint/visitor-keys": "8.59.0", + "@typescript-eslint/project-service": "8.65.0", + "@typescript-eslint/tsconfig-utils": "8.65.0", + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/visitor-keys": "8.65.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", @@ -11997,13 +12192,13 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.0.tgz", - "integrity": "sha512-/uejZt4dSere1bx12WLlPfv8GktzcaDtuJ7s42/HEZ5zGj9oxRaD4bj7qwSunXkf+pbAhFt2zjpHYUiT5lHf0Q==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.65.0.tgz", + "integrity": "sha512-8C71BQkGjiMmXtop7pHVJu1l2NNShFdkCyD6a2ezzs5vU/L3LRtb69EtcteFwz0mYMPzIgOw0n6OV4VBUWZd7A==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.59.0", + "@typescript-eslint/types": "8.65.0", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -12025,16 +12220,16 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/brace-expansion": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", - "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz", + "integrity": "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, "engines": { - "node": "18 || 20 || >=22" + "node": "20 || >=22" } }, "node_modules/@typescript-eslint/parser/node_modules/eslint-visitor-keys": { @@ -12051,13 +12246,13 @@ } }, "node_modules/@typescript-eslint/parser/node_modules/minimatch": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^5.0.5" + "brace-expansion": "^5.0.8" }, "engines": { "node": "18 || 20 || >=22" @@ -12080,14 +12275,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.57.2.tgz", - "integrity": "sha512-FuH0wipFywXRTHf+bTTjNyuNQQsQC3qh/dYzaM4I4W0jrCqjCVuUh99+xd9KamUfmCGPvbO8NDngo/vsnNVqgw==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.65.0.tgz", + "integrity": "sha512-SxnPhbTsGahizDgbu7oqFH/xVtzIqMd/s+WtnSxNxJZJpLbdT5IPdzg8EZxO3+PoKahXmwJLeNQOpKJb3/bi7Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.57.2", - "@typescript-eslint/types": "^8.57.2", + "@typescript-eslint/tsconfig-utils": "^8.65.0", + "@typescript-eslint/types": "^8.65.0", "debug": "^4.4.3" }, "engines": { @@ -12098,13 +12293,13 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/project-service/node_modules/@typescript-eslint/types": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.2.tgz", - "integrity": "sha512-/iZM6FnM4tnx9csuTxspMW4BOSegshwX5oBDznJ7S4WggL7Vczz5d2W11ecc4vRrQMQHXRSxzrCsyG5EsPPTbA==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.65.0.tgz", + "integrity": "sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg==", "dev": true, "license": "MIT", "engines": { @@ -12134,9 +12329,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.2.tgz", - "integrity": "sha512-3Lm5DSM+DCowsUOJC+YqHHnKEfFh5CoGkj5Z31NQSNF4l5wdOwqGn99wmwN/LImhfY3KJnmordBq/4+VDe2eKw==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.65.0.tgz", + "integrity": "sha512-j6GzGqCiRdA7Qhur2VVmKZAkBLfnHFQfx4TaJGL9RMveZqCo48jSHHO0DTgizEnGhtWnqmbtCUSrqSkdiY/0Hg==", "dev": true, "license": "MIT", "engines": { @@ -12147,19 +12342,19 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.0.tgz", - "integrity": "sha512-3TRiZaQSltGqGeNrJzzr1+8YcEobKH9rHnqIp/1psfKFmhRQDNMGP5hBufanYTGznwShzVLs3Mz+gDN7HkWfXg==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.65.0.tgz", + "integrity": "sha512-YjaZ7PRI5qY7ax2L3PbvX0rRyGtipAReCWs0mhhDBHjH/vl0g0BonaGXrKdKpMbIIsMIwDgbk/xzkBTyAltS5g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.59.0", - "@typescript-eslint/typescript-estree": "8.59.0", - "@typescript-eslint/utils": "8.59.0", + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/typescript-estree": "8.65.0", + "@typescript-eslint/utils": "8.65.0", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, @@ -12175,49 +12370,10 @@ "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/project-service": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.0.tgz", - "integrity": "sha512-Lw5ITrR5s5TbC19YSvlr63ZfLaJoU6vtKTHyB0GQOpX0W7d5/Ir6vUahWi/8Sps/nOukZQ0IB3SmlxZnjaKVnw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.59.0", - "@typescript-eslint/types": "^8.59.0", - "debug": "^4.4.3" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.0.tgz", - "integrity": "sha512-91Sbl3s4Kb3SybliIY6muFBmHVv+pYXfybC4Oolp3dvk8BvIE3wOPc+403CWIT7mJNkfQRGtdqghzs2+Z91Tqg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" - } - }, "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.0.tgz", - "integrity": "sha512-nLzdsT1gdOgFxxxwrlNVUBzSNBEEHJ86bblmk4QAS6stfig7rcJzWKqCyxFy3YRRHXDWEkb2NralA1nOYkkm/A==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.65.0.tgz", + "integrity": "sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg==", "dev": true, "license": "MIT", "engines": { @@ -12229,16 +12385,16 @@ } }, "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.0.tgz", - "integrity": "sha512-O9Re9P1BmBLFJyikRbQpLku/QA3/AueZNO9WePLBwQrvkixTmDe8u76B6CYUAITRl/rHawggEqUGn5QIkVRLMw==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.65.0.tgz", + "integrity": "sha512-JboAE2swaYt4tb1fHhHTABE2K+OLy09XfcTbhnk4Pw96f9dd2e9iYsJ28gBggHlo5z5x1rkyWvcPoTuNTd4oGg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.59.0", - "@typescript-eslint/tsconfig-utils": "8.59.0", - "@typescript-eslint/types": "8.59.0", - "@typescript-eslint/visitor-keys": "8.59.0", + "@typescript-eslint/project-service": "8.65.0", + "@typescript-eslint/tsconfig-utils": "8.65.0", + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/visitor-keys": "8.65.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", @@ -12257,13 +12413,13 @@ } }, "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.0.tgz", - "integrity": "sha512-/uejZt4dSere1bx12WLlPfv8GktzcaDtuJ7s42/HEZ5zGj9oxRaD4bj7qwSunXkf+pbAhFt2zjpHYUiT5lHf0Q==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.65.0.tgz", + "integrity": "sha512-8C71BQkGjiMmXtop7pHVJu1l2NNShFdkCyD6a2ezzs5vU/L3LRtb69EtcteFwz0mYMPzIgOw0n6OV4VBUWZd7A==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.59.0", + "@typescript-eslint/types": "8.65.0", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -12285,16 +12441,16 @@ } }, "node_modules/@typescript-eslint/type-utils/node_modules/brace-expansion": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", - "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz", + "integrity": "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, "engines": { - "node": "18 || 20 || >=22" + "node": "20 || >=22" } }, "node_modules/@typescript-eslint/type-utils/node_modules/eslint-visitor-keys": { @@ -12311,13 +12467,13 @@ } }, "node_modules/@typescript-eslint/type-utils/node_modules/minimatch": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^5.0.5" + "brace-expansion": "^5.0.8" }, "engines": { "node": "18 || 20 || >=22" @@ -12409,16 +12565,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.0.tgz", - "integrity": "sha512-I1R/K7V07XsMJ12Oaxg/O9GfrysGTmCRhvZJBv0RE0NcULMzjqVpR5kRRQjHsz3J/bElU7HwCO7zkqL+MSUz+g==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.65.0.tgz", + "integrity": "sha512-gXiwIHsYreboxeJucHKPvgwl7dXt50mF8s1/c00cP/WoVTyWKFdtfhRWwZiXYFU5H2O8vVoSLNrexFZjYS/SGA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.59.0", - "@typescript-eslint/types": "8.59.0", - "@typescript-eslint/typescript-estree": "8.59.0" + "@typescript-eslint/scope-manager": "8.65.0", + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/typescript-estree": "8.65.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -12432,37 +12588,15 @@ "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/project-service": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.0.tgz", - "integrity": "sha512-Lw5ITrR5s5TbC19YSvlr63ZfLaJoU6vtKTHyB0GQOpX0W7d5/Ir6vUahWi/8Sps/nOukZQ0IB3SmlxZnjaKVnw==", + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/scope-manager": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.65.0.tgz", + "integrity": "sha512-Esbl8OSYiVxBokYgWPf7VVWg/BE798wXhimnn9ML9Pt5qoDf8bfQlgjlKXR/k98+AcNzlLKYrpCcrcuZ9DZLgg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.59.0", - "@typescript-eslint/types": "^8.59.0", - "debug": "^4.4.3" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/scope-manager": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.0.tgz", - "integrity": "sha512-UzR16Ut8IpA3Mc4DbgAShlPPkVm8xXMWafXxB0BocaVRHs8ZGakAxGRskF7FId3sdk9lgGD73GSFaWmWFDE4dg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.59.0", - "@typescript-eslint/visitor-keys": "8.59.0" + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/visitor-keys": "8.65.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -12472,27 +12606,10 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.0.tgz", - "integrity": "sha512-91Sbl3s4Kb3SybliIY6muFBmHVv+pYXfybC4Oolp3dvk8BvIE3wOPc+403CWIT7mJNkfQRGtdqghzs2+Z91Tqg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" - } - }, "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.0.tgz", - "integrity": "sha512-nLzdsT1gdOgFxxxwrlNVUBzSNBEEHJ86bblmk4QAS6stfig7rcJzWKqCyxFy3YRRHXDWEkb2NralA1nOYkkm/A==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.65.0.tgz", + "integrity": "sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg==", "dev": true, "license": "MIT", "engines": { @@ -12504,16 +12621,16 @@ } }, "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.0.tgz", - "integrity": "sha512-O9Re9P1BmBLFJyikRbQpLku/QA3/AueZNO9WePLBwQrvkixTmDe8u76B6CYUAITRl/rHawggEqUGn5QIkVRLMw==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.65.0.tgz", + "integrity": "sha512-JboAE2swaYt4tb1fHhHTABE2K+OLy09XfcTbhnk4Pw96f9dd2e9iYsJ28gBggHlo5z5x1rkyWvcPoTuNTd4oGg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.59.0", - "@typescript-eslint/tsconfig-utils": "8.59.0", - "@typescript-eslint/types": "8.59.0", - "@typescript-eslint/visitor-keys": "8.59.0", + "@typescript-eslint/project-service": "8.65.0", + "@typescript-eslint/tsconfig-utils": "8.65.0", + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/visitor-keys": "8.65.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", @@ -12532,13 +12649,13 @@ } }, "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.59.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.0.tgz", - "integrity": "sha512-/uejZt4dSere1bx12WLlPfv8GktzcaDtuJ7s42/HEZ5zGj9oxRaD4bj7qwSunXkf+pbAhFt2zjpHYUiT5lHf0Q==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.65.0.tgz", + "integrity": "sha512-8C71BQkGjiMmXtop7pHVJu1l2NNShFdkCyD6a2ezzs5vU/L3LRtb69EtcteFwz0mYMPzIgOw0n6OV4VBUWZd7A==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.59.0", + "@typescript-eslint/types": "8.65.0", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -12560,16 +12677,16 @@ } }, "node_modules/@typescript-eslint/utils/node_modules/brace-expansion": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", - "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz", + "integrity": "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, "engines": { - "node": "18 || 20 || >=22" + "node": "20 || >=22" } }, "node_modules/@typescript-eslint/utils/node_modules/eslint-visitor-keys": { @@ -12586,13 +12703,13 @@ } }, "node_modules/@typescript-eslint/utils/node_modules/minimatch": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^5.0.5" + "brace-expansion": "^5.0.8" }, "engines": { "node": "18 || 20 || >=22" @@ -12632,6 +12749,349 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@unrs/resolver-binding-android-arm-eabi": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.12.2.tgz", + "integrity": "sha512-g5T90pqg1bo/7mytQx6F4iBNC0Wsh9cu+z9veDbFjc7HjpesJFWD7QMS0NGStXM075+7dJPPVvBbpZlnrdpi/w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-android-arm64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.12.2.tgz", + "integrity": "sha512-YGCRZv/9GLhwmz6mYDeTsm/92BAyR28l6c2ReweVW5pWgfsitWLY8upvfRlGdoyD8HjeTHSYJWyZGD4KJA/nFQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-arm64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.12.2.tgz", + "integrity": "sha512-u9DiNT1auQMO20A9SyTuG3wUgQWB9Z7KjAg0uFuCDR1FsAY8A0CG2S6JpHS1xwm/w1G08bjXZDcyOCjv1WAm2w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-x64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.12.2.tgz", + "integrity": "sha512-f7rPLi/T1HVKZu/u6t87lroib16n8vrSzcyxI7lg4BGO9UF26KhQL44sd9eOUgrTYhvRXtWOIZT5PejdPyJfUA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-freebsd-x64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.12.2.tgz", + "integrity": "sha512-BpcOjWCJub6nRZUS2zA20pmLvjtqAtGejETaIyRLiZiQf++cbrjltLA5NN/xaXfqeOBOSlMFbemIl5/S5tljmg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.12.2.tgz", + "integrity": "sha512-vZTDvdSISZjJx66OzJqtsOhzifbqRjbmI1Mnu49fQDwog5GtDI4QidRiEAYbZCRj9C8YZEW+3ZjqsyS9GR4k2A==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.12.2.tgz", + "integrity": "sha512-BiPI+IrIlwcW4nLLMM21+B1dFPzd55yAVgVGrdgDjNef+ch03GdxrcyaIz8X9SsQirh/kCQ7mviyWlMxdh2D7g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.12.2.tgz", + "integrity": "sha512-zJc0H99FEPoFfSrNpa91HYfxzfAJCr502oxNK1cfdC9hlaFI43RT+JFCann9JUgZmLzzntChHyn13Sgn9ljHNg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-musl": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.12.2.tgz", + "integrity": "sha512-KQ3Lki6l+Pz1k/eBipN41ES+YUK30beLGb9YqcB1O542cyLCNE6GaxrfcY3T6EezmGGk84wb5XyO9loTM9tkcA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-loong64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-loong64-gnu/-/resolver-binding-linux-loong64-gnu-1.12.2.tgz", + "integrity": "sha512-3SJGEh1DborhG6pyxvhPzCT4bbSIVihsvgJc13P1bHG7KLdNDaF9T3gsTwFc7Jw/5Y5/iWOjkEx7Zy0NvCGX3Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-loong64-musl": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-loong64-musl/-/resolver-binding-linux-loong64-musl-1.12.2.tgz", + "integrity": "sha512-jiuG/Obbel7uw1PwHNFfrkiKhLAF6mnyZ6aWlOAVN9WqKm8v0OFGnciJIHu8+CMvXLQ8AD51LPzAoUfT21D5Ew==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.12.2.tgz", + "integrity": "sha512-q7xRvVpmcfeL+LlZg8Pbbo6QaTZwDU5BaGZbwfhkEsXJn3Was8xYfE0RBH266xZt0rM6B7i8xAYIvjthuUIWHg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.12.2.tgz", + "integrity": "sha512-0CVdx6lcnT3Q9inOH8tsMIOJ6ImndllMjqJHg8RLVdB7Vq4SfkEXl9mCSsVNuNA4MCYycRicCUxPCabVHJRr6A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.12.2.tgz", + "integrity": "sha512-iOwlRo9vnp6R6ohHQS11n0NnfdXx/omhkocmIfaPRpQhKZ+3BDMkkdRVh53qjkFkpPddf+FETA28NwGN7l5l+w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.12.2.tgz", + "integrity": "sha512-HYJtLfXq94q8iZNFT1lknx258wlkkWhZeUXJRqzKBBUJ00CvZ+N33zgbCqimLjsyw5Va6uUxhVa12mI+kaveEw==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.12.2.tgz", + "integrity": "sha512-mPsUhunKKDih5O96Y6enDQyHc1SqBPlY1E/SfMWDM3EdJ95Z9CArPeCVwCCqbP45ljvivdEk8Fxn+SIb1rDAJQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-musl": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.12.2.tgz", + "integrity": "sha512-azrt6+5ydLd8Vt210AAFis/lZevSfPw93EJRIJG+xPu4WCJ8K0kppCTpMyLPcKT7H15M4Jnt2tMp5bOvCkRC6A==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-openharmony-arm64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-openharmony-arm64/-/resolver-binding-openharmony-arm64-1.12.2.tgz", + "integrity": "sha512-YZ9hP4O0X9PQb8eO980qmLNGH4zT3I9+SZTdt0Pr0YyuGQhYKoOZkV02VzrzyOZJ5xIJ3UFIenKkUkGg8GjgWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@unrs/resolver-binding-wasm32-wasi": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.12.2.tgz", + "integrity": "sha512-tYFDIkMxSflfEc/h92ZWNsZlHSwgimbNHSO3PL2JWQHfCuC2q316jMyYU9TIWZsFK2bQwyK5VAdYgn8ygPj69A==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.10.0", + "@emnapi/runtime": "1.10.0", + "@napi-rs/wasm-runtime": "^1.1.4" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.12.2.tgz", + "integrity": "sha512-qzNyg3xL0VPQmCaUh+N5jSitce6k+uCBfMDesWRnlULOZaqUkaJ0ybdT+UqlAWJoQjuqfIU/0Ptx9bteN4D82g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.12.2.tgz", + "integrity": "sha512-WD9sY00OfpHVGfsnHZoA8jVT+esS/Bg8z8jzxp5BnDCjjwsuKsPQrzswwpFy4J1AUJbXPRfkpcX0mXrzeXW79g==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-x64-msvc": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.12.2.tgz", + "integrity": "sha512-nAB74NfSNKknqQ1RrYj6uz8FcXEomu/MATJZxh/x+BArzN2U3JbOYC0APYzUIGhVY3m5hRxA8VPNdPBoG8txlA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@whatwg-node/promise-helpers": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/@whatwg-node/promise-helpers/-/promise-helpers-1.3.2.tgz", @@ -12701,9 +13161,9 @@ } }, "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", + "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", "dev": true, "license": "MIT", "bin": { @@ -13163,26 +13623,30 @@ } }, "node_modules/babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", + "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" }, "peerDependencies": { - "@babel/core": "^7.0.0" + "@babel/core": "^7.0.0 || ^8.0.0-0" } }, "node_modules/babel-preset-jest": { @@ -13615,9 +14079,9 @@ } }, "node_modules/comment-parser": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.5.tgz", - "integrity": "sha512-aRDkn3uyIlCFfk5NUA+VdwMmMsh8JGhc4hapfV4yxymHGQ3BVskMQfoXGpCo5IoBuQ9tS5iiVKhCpTcB4pW4qw==", + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.6.tgz", + "integrity": "sha512-ObxuY6vnbWTN6Od72xfwN9DbzC7Y2vv8u1Soi9ahRKL37gb6y1qk6/dgjs+3JWuXJHWvsg3BXIwzd/rkmAwavg==", "dev": true, "license": "MIT", "engines": { @@ -13956,10 +14420,11 @@ } }, "node_modules/dedent": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", - "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==", + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.2.tgz", + "integrity": "sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==", "dev": true, + "license": "MIT", "peerDependencies": { "babel-plugin-macros": "^3.1.0" }, @@ -14208,19 +14673,6 @@ "once": "^1.4.0" } }, - "node_modules/enhanced-resolve": { - "version": "5.15.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", - "integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, "node_modules/error-ex": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", @@ -14403,9 +14855,9 @@ } }, "node_modules/eslint": { - "version": "9.39.4", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", - "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", + "version": "9.39.5", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.5.tgz", + "integrity": "sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==", "dev": true, "license": "MIT", "dependencies": { @@ -14414,8 +14866,8 @@ "@eslint/config-array": "^0.21.2", "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", - "@eslint/eslintrc": "^3.3.5", - "@eslint/js": "9.39.4", + "@eslint/eslintrc": "^3.3.6", + "@eslint/js": "9.39.5", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", @@ -14485,28 +14937,38 @@ } }, "node_modules/eslint-import-resolver-typescript": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.6.0.tgz", - "integrity": "sha512-QTHR9ddNnn35RTxlaEnx2gCxqFlF2SEN0SE2d17SqwyM7YOSI2GHWRYp5BiRkObTUNYPupC/3Fq2a0PpT+EKpg==", + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.1.tgz", + "integrity": "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==", "dev": true, + "license": "ISC", "dependencies": { - "debug": "^4.3.4", - "enhanced-resolve": "^5.12.0", - "eslint-module-utils": "^2.7.4", - "fast-glob": "^3.3.1", - "get-tsconfig": "^4.5.0", - "is-core-module": "^2.11.0", - "is-glob": "^4.0.3" + "@nolyfill/is-core-module": "1.0.39", + "debug": "^4.4.0", + "get-tsconfig": "^4.10.0", + "is-bun-module": "^2.0.0", + "stable-hash": "^0.0.5", + "tinyglobby": "^0.2.13", + "unrs-resolver": "^1.6.2" }, "engines": { "node": "^14.18.0 || >=16.0.0" }, "funding": { - "url": "https://opencollective.com/unts/projects/eslint-import-resolver-ts" + "url": "https://opencollective.com/eslint-import-resolver-typescript" }, "peerDependencies": { "eslint": "*", - "eslint-plugin-import": "*" + "eslint-plugin-import": "*", + "eslint-plugin-import-x": "*" + }, + "peerDependenciesMeta": { + "eslint-plugin-import": { + "optional": true + }, + "eslint-plugin-import-x": { + "optional": true + } } }, "node_modules/eslint-module-utils": { @@ -14557,9 +15019,9 @@ } }, "node_modules/eslint-plugin-functional": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/eslint-plugin-functional/-/eslint-plugin-functional-9.0.4.tgz", - "integrity": "sha512-zm4qaoqb2r50V4WXxt0Mj92buXGMECYvMxGQ6sSb+XeJ+Eec6zCHuMY2+AWK1mqiApvUz2tCtp1P3zcEPU0huw==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-functional/-/eslint-plugin-functional-9.0.5.tgz", + "integrity": "sha512-RYbNpGkGu5mSN7xlezyeE7TE3S9rsliAS5FOorZoxn7126UTslcQnoVOIAOA6fMOlz6dNG3x3cd3tuzrHM6mRA==", "dev": true, "funding": [ { @@ -14684,24 +15146,24 @@ } }, "node_modules/eslint-plugin-jsdoc": { - "version": "62.5.4", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-62.5.4.tgz", - "integrity": "sha512-U+Q5ppErmC17VFQl542eBIaXcuq975BzoIHBXyx7UQx/i4gyHXxPiBkonkuxWyFA98hGLALLUuD+NJcXqSGKxg==", + "version": "62.9.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-62.9.0.tgz", + "integrity": "sha512-PY7/X4jrVgoIDncUmITlUqK546Ltmx/Pd4Hdsu4CvSjryQZJI2mEV4vrdMufyTetMiZ5taNSqvK//BTgVUlNkA==", "dev": true, "license": "BSD-3-Clause", "dependencies": { - "@es-joy/jsdoccomment": "~0.84.0", + "@es-joy/jsdoccomment": "~0.86.0", "@es-joy/resolve.exports": "1.2.0", "are-docs-informative": "^0.0.2", - "comment-parser": "1.4.5", + "comment-parser": "1.4.6", "debug": "^4.4.3", "escape-string-regexp": "^4.0.0", - "espree": "^11.1.0", + "espree": "^11.2.0", "esquery": "^1.7.0", "html-entities": "^2.6.0", "object-deep-merge": "^2.0.0", "parse-imports-exports": "^0.2.4", - "semver": "^7.7.3", + "semver": "^7.7.4", "spdx-expression-parse": "^4.0.0", "to-valid-identifier": "^1.0.0" }, @@ -14709,13 +15171,13 @@ "node": "^20.19.0 || ^22.13.0 || >=24" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0" + "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0" } }, "node_modules/eslint-plugin-jsdoc/node_modules/eslint-visitor-keys": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.0.tgz", - "integrity": "sha512-A0XeIi7CXU7nPlfHS9loMYEKxUaONu/hTEzHTGba9Huu94Cq1hPivf+DE5erJozZOky0LfvXAyrV/tcswpLI0Q==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -14726,15 +15188,15 @@ } }, "node_modules/eslint-plugin-jsdoc/node_modules/espree": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-11.1.0.tgz", - "integrity": "sha512-WFWYhO1fV4iYkqOOvq8FbqIhr2pYfoDY0kCotMkDeNtGpiGGkZ1iov2u8ydjtgM8yF8rzK7oaTbw2NAzbAbehw==", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "acorn": "^8.15.0", + "acorn": "^8.16.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^5.0.0" + "eslint-visitor-keys": "^5.0.1" }, "engines": { "node": "^20.19.0 || ^22.13.0 || >=24" @@ -14773,9 +15235,9 @@ } }, "node_modules/eslint-plugin-promise": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-7.2.1.tgz", - "integrity": "sha512-SWKjd+EuvWkYaS+uN2csvj0KoP43YTu7+phKQ5v+xw6+A0gutVX2yqCeCkC3uLCJFiPfR2dD8Es5L7yUsmvEaA==", + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-7.3.0.tgz", + "integrity": "sha512-6uGiOR0INuujr6PEQmeSSP7GbIMJ/ebEXXiEzb/nOj68LknH5Pxzb/AbZivmr6VE6TkTE8rTjRK9zhKpK6HsRA==", "dev": true, "license": "ISC", "dependencies": { @@ -14788,7 +15250,7 @@ "url": "https://opencollective.com/eslint" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0" + "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0" } }, "node_modules/eslint-plugin-security": { @@ -14802,10 +15264,11 @@ } }, "node_modules/eslint-plugin-switch-statement": { - "version": "0.0.11", - "resolved": "https://registry.npmjs.org/eslint-plugin-switch-statement/-/eslint-plugin-switch-statement-0.0.11.tgz", - "integrity": "sha512-hjZ1D0Ri6IYG5raNOy/dyVDO/BX9p/9d0RLR9JrctTjFWoj9tozb7vu2d683b850Hy9OPGMB0Kvn3Ee3uWostQ==", + "version": "0.0.12", + "resolved": "https://registry.npmjs.org/eslint-plugin-switch-statement/-/eslint-plugin-switch-statement-0.0.12.tgz", + "integrity": "sha512-LJd6sTnm/nptvFcHfCau6Kx946LPBMFhF8Gz1gEPhlt5Kzv8wIEYCaut1W9xYksDKwbvk8Ir3QBZKRnzCO+R2g==", "dev": true, + "license": "ISC", "dependencies": { "@typescript-eslint/utils": "^7.4.0" }, @@ -15038,7 +15501,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/eventemitter-asyncresource/-/eventemitter-asyncresource-1.0.0.tgz", "integrity": "sha512-39F7TBIV0G7gTelxwbEqnwhp90eqCPON1k0NwNfwhgKn4Co4ybUbj2pECcXT0B3ztRKZ7Pw1JujUUgmQJHcVAQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/execa": { "version": "5.1.1", @@ -16230,6 +16694,7 @@ "resolved": "https://registry.npmjs.org/hdr-histogram-js/-/hdr-histogram-js-2.0.3.tgz", "integrity": "sha512-Hkn78wwzWHNCp2uarhzQ2SGFLU3JY8SBDDd3TAABK4fc30wm+MuPOrg5QVFVfkKOQd6Bfz3ukJEI+q9sXEkK1g==", "dev": true, + "license": "BSD", "dependencies": { "@assemblyscript/loader": "^0.10.1", "base64-js": "^1.2.0", @@ -16240,7 +16705,8 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/hdr-histogram-percentiles-obj/-/hdr-histogram-percentiles-obj-3.0.0.tgz", "integrity": "sha512-7kIufnBqdsBGcSZLPJwqHT3yhk1QTsSlFsVD3kx5ixH/AlgBs9yM1q6DPhXZ8f8gtdqgh7N7/5btRLpQsS2gHw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/heap": { "version": "0.2.7", @@ -16556,6 +17022,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-bun-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz", + "integrity": "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.7.1" + } + }, "node_modules/is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", @@ -17400,6 +17876,7 @@ "resolved": "https://registry.npmjs.org/jest-light-runner/-/jest-light-runner-0.4.1.tgz", "integrity": "sha512-xO9ryg9X2LbUM5FN98xcaoYSdSgzEUoJO3yROiebsmtnEx/uH4zayM0vq8OgzLlpZcOpmNouJ4CI561GRQNb9w==", "dev": true, + "license": "MIT", "dependencies": { "@jest/expect": "^29.0.1", "jest-circus": "^29.0.1", @@ -17421,6 +17898,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.4.0.tgz", "integrity": "sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -17825,9 +18303,9 @@ } }, "node_modules/jsdoc-type-pratt-parser": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-7.1.1.tgz", - "integrity": "sha512-/2uqY7x6bsrpi3i9LVU6J89352C0rpMk0as8trXxCtvd4kPk1ke/Eyif6wqfSLvoNJqcDG9Vk4UsXgygzCt2xA==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-7.2.0.tgz", + "integrity": "sha512-dh140MMgjyg3JhJZY/+iEzW+NO5xR2gpbDFKHqotCmexElVntw7GjWjt511+C/Ef02RU5TKYrJo/Xlzk+OLaTw==", "dev": true, "license": "MIT", "engines": { @@ -17986,9 +18464,9 @@ } }, "node_modules/knip": { - "version": "6.16.1", - "resolved": "https://registry.npmjs.org/knip/-/knip-6.16.1.tgz", - "integrity": "sha512-TKMn1rxgH6h9vXR9Y0B+Cq7AdPTr9EI02IwoT65NzqYUkvoDQAaJ/aPybiFpAhZ1px6cNYYwXf86iHkBgzCo9w==", + "version": "6.27.0", + "resolved": "https://registry.npmjs.org/knip/-/knip-6.27.0.tgz", + "integrity": "sha512-CngYEYrD0n20N06FXA8n3u/0Wnnugoa+B9k14OP+iKIgkCHuzvIdsP3nfwjhByoc1WfogpxfiriMboAXFETDUw==", "dev": true, "funding": [ { @@ -18006,13 +18484,13 @@ "formatly": "^0.3.0", "get-tsconfig": "4.14.0", "jiti": "^2.7.0", - "oxc-parser": "^0.133.0", - "oxc-resolver": "^11.20.0", + "oxc-parser": "^0.137.0", + "oxc-resolver": "11.21.3", "picomatch": "^4.0.4", "smol-toml": "^1.6.1", "strip-json-comments": "5.0.3", - "tinyglobby": "^0.2.16", - "unbash": "^3.0.0", + "tinyglobby": "^0.2.17", + "unbash": "^4.0.1", "yaml": "^2.9.0", "zod": "^4.1.11" }, @@ -18435,6 +18913,22 @@ "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3" } }, + "node_modules/napi-postinstall": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz", + "integrity": "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==", + "dev": true, + "license": "MIT", + "bin": { + "napi-postinstall": "lib/cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/napi-postinstall" + } + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -18456,6 +18950,7 @@ "integrity": "sha512-px/KnJAJZf5RuBGcfD+Sp2pAKq0ytz8j+1NehvgIGFkvtvFrDM3T8E4x/JJODXK9WZow8RRGrbA9QQ3hs+pDhA==", "dev": true, "hasInstallScript": true, + "license": "MIT", "optional": true, "os": [ "!win32" @@ -18465,30 +18960,19 @@ "node-gyp-build": "^4.2.2" } }, - "node_modules/nice-napi/node_modules/node-addon-api": { + "node_modules/node-abort-controller": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", + "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==" + }, + "node_modules/node-addon-api": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", "dev": true, + "license": "MIT", "optional": true }, - "node_modules/nice-napi/node_modules/node-gyp-build": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.0.tgz", - "integrity": "sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==", - "dev": true, - "optional": true, - "bin": { - "node-gyp-build": "bin.js", - "node-gyp-build-optional": "optional.js", - "node-gyp-build-test": "build-test.js" - } - }, - "node_modules/node-abort-controller": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", - "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==" - }, "node_modules/node-cleanup": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/node-cleanup/-/node-cleanup-2.1.2.tgz", @@ -18513,6 +18997,19 @@ "node": ">=10.5.0" } }, + "node_modules/node-gyp-build": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", + "dev": true, + "license": "MIT", + "optional": true, + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, "node_modules/node-gyp-build-optional-packages": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz", @@ -18818,13 +19315,13 @@ } }, "node_modules/oxc-parser": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/oxc-parser/-/oxc-parser-0.133.0.tgz", - "integrity": "sha512-661RSx+ZcjBmjBYid+Fpp/2F5EbtildpeoZh5HdgnGs+jZ03nqQEQW8yGkt4BGyOC3OMPDQQRl8M5kqD2/g6jw==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/oxc-parser/-/oxc-parser-0.137.0.tgz", + "integrity": "sha512-yFImD+WLElJpLKy8llG1qe4DCmMsL18peRp8XP1JKfig/gISbJkglnpDtX2aTmAn10kZF7164HbN2H8QPsXxGg==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "^0.133.0" + "@oxc-project/types": "^0.137.0" }, "engines": { "node": "^20.19.0 || >=22.12.0" @@ -18833,57 +19330,57 @@ "url": "https://github.com/sponsors/Boshen" }, "optionalDependencies": { - "@oxc-parser/binding-android-arm-eabi": "0.133.0", - "@oxc-parser/binding-android-arm64": "0.133.0", - "@oxc-parser/binding-darwin-arm64": "0.133.0", - "@oxc-parser/binding-darwin-x64": "0.133.0", - "@oxc-parser/binding-freebsd-x64": "0.133.0", - "@oxc-parser/binding-linux-arm-gnueabihf": "0.133.0", - "@oxc-parser/binding-linux-arm-musleabihf": "0.133.0", - "@oxc-parser/binding-linux-arm64-gnu": "0.133.0", - "@oxc-parser/binding-linux-arm64-musl": "0.133.0", - "@oxc-parser/binding-linux-ppc64-gnu": "0.133.0", - "@oxc-parser/binding-linux-riscv64-gnu": "0.133.0", - "@oxc-parser/binding-linux-riscv64-musl": "0.133.0", - "@oxc-parser/binding-linux-s390x-gnu": "0.133.0", - "@oxc-parser/binding-linux-x64-gnu": "0.133.0", - "@oxc-parser/binding-linux-x64-musl": "0.133.0", - "@oxc-parser/binding-openharmony-arm64": "0.133.0", - "@oxc-parser/binding-wasm32-wasi": "0.133.0", - "@oxc-parser/binding-win32-arm64-msvc": "0.133.0", - "@oxc-parser/binding-win32-ia32-msvc": "0.133.0", - "@oxc-parser/binding-win32-x64-msvc": "0.133.0" + "@oxc-parser/binding-android-arm-eabi": "0.137.0", + "@oxc-parser/binding-android-arm64": "0.137.0", + "@oxc-parser/binding-darwin-arm64": "0.137.0", + "@oxc-parser/binding-darwin-x64": "0.137.0", + "@oxc-parser/binding-freebsd-x64": "0.137.0", + "@oxc-parser/binding-linux-arm-gnueabihf": "0.137.0", + "@oxc-parser/binding-linux-arm-musleabihf": "0.137.0", + "@oxc-parser/binding-linux-arm64-gnu": "0.137.0", + "@oxc-parser/binding-linux-arm64-musl": "0.137.0", + "@oxc-parser/binding-linux-ppc64-gnu": "0.137.0", + "@oxc-parser/binding-linux-riscv64-gnu": "0.137.0", + "@oxc-parser/binding-linux-riscv64-musl": "0.137.0", + "@oxc-parser/binding-linux-s390x-gnu": "0.137.0", + "@oxc-parser/binding-linux-x64-gnu": "0.137.0", + "@oxc-parser/binding-linux-x64-musl": "0.137.0", + "@oxc-parser/binding-openharmony-arm64": "0.137.0", + "@oxc-parser/binding-wasm32-wasi": "0.137.0", + "@oxc-parser/binding-win32-arm64-msvc": "0.137.0", + "@oxc-parser/binding-win32-ia32-msvc": "0.137.0", + "@oxc-parser/binding-win32-x64-msvc": "0.137.0" } }, "node_modules/oxc-resolver": { - "version": "11.20.0", - "resolved": "https://registry.npmjs.org/oxc-resolver/-/oxc-resolver-11.20.0.tgz", - "integrity": "sha512-CblytBiV/a/ZXY34dsVU2NxhIOxMXst8CvDCtyBelVITgd7PLrKzbEbA6oKLdPjvDKDzCiW48qzmzZ+mYaqn+g==", + "version": "11.21.3", + "resolved": "https://registry.npmjs.org/oxc-resolver/-/oxc-resolver-11.21.3.tgz", + "integrity": "sha512-2Mx3fKQz7+xgrBONjsxOgCGtMHOn38/HxMzW1I5efwXB5a4lRN0Vp40gYUJFBWJslcrvwoofTrqoTnLbwTd3pA==", "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/Boshen" }, "optionalDependencies": { - "@oxc-resolver/binding-android-arm-eabi": "11.20.0", - "@oxc-resolver/binding-android-arm64": "11.20.0", - "@oxc-resolver/binding-darwin-arm64": "11.20.0", - "@oxc-resolver/binding-darwin-x64": "11.20.0", - "@oxc-resolver/binding-freebsd-x64": "11.20.0", - "@oxc-resolver/binding-linux-arm-gnueabihf": "11.20.0", - "@oxc-resolver/binding-linux-arm-musleabihf": "11.20.0", - "@oxc-resolver/binding-linux-arm64-gnu": "11.20.0", - "@oxc-resolver/binding-linux-arm64-musl": "11.20.0", - "@oxc-resolver/binding-linux-ppc64-gnu": "11.20.0", - "@oxc-resolver/binding-linux-riscv64-gnu": "11.20.0", - "@oxc-resolver/binding-linux-riscv64-musl": "11.20.0", - "@oxc-resolver/binding-linux-s390x-gnu": "11.20.0", - "@oxc-resolver/binding-linux-x64-gnu": "11.20.0", - "@oxc-resolver/binding-linux-x64-musl": "11.20.0", - "@oxc-resolver/binding-openharmony-arm64": "11.20.0", - "@oxc-resolver/binding-wasm32-wasi": "11.20.0", - "@oxc-resolver/binding-win32-arm64-msvc": "11.20.0", - "@oxc-resolver/binding-win32-x64-msvc": "11.20.0" + "@oxc-resolver/binding-android-arm-eabi": "11.21.3", + "@oxc-resolver/binding-android-arm64": "11.21.3", + "@oxc-resolver/binding-darwin-arm64": "11.21.3", + "@oxc-resolver/binding-darwin-x64": "11.21.3", + "@oxc-resolver/binding-freebsd-x64": "11.21.3", + "@oxc-resolver/binding-linux-arm-gnueabihf": "11.21.3", + "@oxc-resolver/binding-linux-arm-musleabihf": "11.21.3", + "@oxc-resolver/binding-linux-arm64-gnu": "11.21.3", + "@oxc-resolver/binding-linux-arm64-musl": "11.21.3", + "@oxc-resolver/binding-linux-ppc64-gnu": "11.21.3", + "@oxc-resolver/binding-linux-riscv64-gnu": "11.21.3", + "@oxc-resolver/binding-linux-riscv64-musl": "11.21.3", + "@oxc-resolver/binding-linux-s390x-gnu": "11.21.3", + "@oxc-resolver/binding-linux-x64-gnu": "11.21.3", + "@oxc-resolver/binding-linux-x64-musl": "11.21.3", + "@oxc-resolver/binding-openharmony-arm64": "11.21.3", + "@oxc-resolver/binding-wasm32-wasi": "11.21.3", + "@oxc-resolver/binding-win32-arm64-msvc": "11.21.3", + "@oxc-resolver/binding-win32-x64-msvc": "11.21.3" } }, "node_modules/p-limit": { @@ -18964,7 +19461,8 @@ "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "dev": true + "dev": true, + "license": "(MIT AND Zlib)" }, "node_modules/parent-module": { "version": "1.0.1", @@ -19285,10 +19783,11 @@ "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==" }, "node_modules/pirates": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", - "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 6" } @@ -19298,6 +19797,7 @@ "resolved": "https://registry.npmjs.org/piscina/-/piscina-3.2.0.tgz", "integrity": "sha512-yn/jMdHRw+q2ZJhFhyqsmANcbF6V2QwmD84c6xRau+QpQOmtrBCoRGdvTfeuFDYXB5W2m6MfLkjkvQa9lUSmIA==", "dev": true, + "license": "MIT", "dependencies": { "eventemitter-asyncresource": "^1.0.0", "hdr-histogram-js": "^2.0.1", @@ -19377,13 +19877,13 @@ } }, "node_modules/playwright": { - "version": "1.61.0", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.61.0.tgz", - "integrity": "sha512-Z+7BeeqQPRRzklHsVFP4KTGIyMxKUmfeRA4WisM6G3/XW6nwGeX6fX9qYaDa+CiUqpOkb2f6X3nar05R3kSuJQ==", + "version": "1.61.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.61.1.tgz", + "integrity": "sha512-DWnY5o3YbLWK4GovuAVwpqL+1VwGNdUGrRr++8j8PtQQzvAVZUIMjKQ90fY689sEJZJBbZVw1rXaOKSTitkzPQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.61.0" + "playwright-core": "1.61.1" }, "bin": { "playwright": "cli.js" @@ -19396,9 +19896,9 @@ } }, "node_modules/playwright-core": { - "version": "1.61.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.61.0.tgz", - "integrity": "sha512-caX7TrY3Ml6egyDX0WUcTHDxodl/b51y5wJOdCEA36QviK/s2g081hvmGs8eaE3DWb6NYZQ6BjO/QkNRPenoPA==", + "version": "1.61.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.61.1.tgz", + "integrity": "sha512-h7Qlt6m4REp25qvIdvbDtVmD4LqVXfpRxhORv9L0jzETM05p4fuPJ3dKyuSXQxDSbXnmS79HAgi9589lGSpLkg==", "dev": true, "license": "Apache-2.0", "bin": { @@ -20536,6 +21036,13 @@ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, + "node_modules/stable-hash": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz", + "integrity": "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==", + "dev": true, + "license": "MIT" + }, "node_modules/stack-utils": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", @@ -20904,15 +21411,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/teeny-request": { "version": "10.1.2", "resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-10.1.2.tgz", @@ -21127,10 +21625,11 @@ } }, "node_modules/ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, + "license": "MIT", "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -21410,6 +21909,7 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.2.tgz", "integrity": "sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew==", "dev": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -21419,113 +21919,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.57.2.tgz", - "integrity": "sha512-VEPQ0iPgWO/sBaZOU1xo4nuNdODVOajPnTIbog2GKYr31nIlZ0fWPoCQgGfF3ETyBl1vn63F/p50Um9Z4J8O8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/eslint-plugin": "8.57.2", - "@typescript-eslint/parser": "8.57.2", - "@typescript-eslint/typescript-estree": "8.57.2", - "@typescript-eslint/utils": "8.57.2" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.57.2.tgz", - "integrity": "sha512-NZZgp0Fm2IkD+La5PR81sd+g+8oS6JwJje+aRWsDocxHkjyRw0J5L5ZTlN3LI1LlOcGL7ph3eaIUmTXMIjLk0w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.57.2", - "@typescript-eslint/type-utils": "8.57.2", - "@typescript-eslint/utils": "8.57.2", - "@typescript-eslint/visitor-keys": "8.57.2", - "ignore": "^7.0.5", - "natural-compare": "^1.4.0", - "ts-api-utils": "^2.4.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^8.57.2", - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/parser": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.57.2.tgz", - "integrity": "sha512-30ScMRHIAD33JJQkgfGW1t8CURZtjc2JpTrq5n2HFhOefbAhb7ucc7xJwdWcrEtqUIYJ73Nybpsggii6GtAHjA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/scope-manager": "8.57.2", - "@typescript-eslint/types": "8.57.2", - "@typescript-eslint/typescript-estree": "8.57.2", - "@typescript-eslint/visitor-keys": "8.57.2", - "debug": "^4.4.3" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/scope-manager": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.57.2.tgz", - "integrity": "sha512-snZKH+W4WbWkrBqj4gUNRIGb/jipDW3qMqVJ4C9rzdFc+wLwruxk+2a5D+uoFcKPAqyqEnSb4l2ULuZf95eSkw==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.65.0.tgz", + "integrity": "sha512-/ggrHAwyjENDusvyxbuqxAC2dTnZg/Z8F+fgQtYIz+L6n/9HfSlEZcFGV/NsMNa6CkGk0xUjUAFwC0vHOflvIA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.57.2", - "@typescript-eslint/visitor-keys": "8.57.2" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/type-utils": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.57.2.tgz", - "integrity": "sha512-Co6ZCShm6kIbAM/s+oYVpKFfW7LBc6FXoPXjTRQ449PPNBY8U0KZXuevz5IFuuUj2H9ss40atTaf9dlGLzbWZg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.57.2", - "@typescript-eslint/typescript-estree": "8.57.2", - "@typescript-eslint/utils": "8.57.2", - "debug": "^4.4.3", - "ts-api-utils": "^2.4.0" + "@typescript-eslint/eslint-plugin": "8.65.0", + "@typescript-eslint/parser": "8.65.0", + "@typescript-eslint/typescript-estree": "8.65.0", + "@typescript-eslint/utils": "8.65.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -21536,13 +21939,13 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/typescript-eslint/node_modules/@typescript-eslint/types": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.2.tgz", - "integrity": "sha512-/iZM6FnM4tnx9csuTxspMW4BOSegshwX5oBDznJ7S4WggL7Vczz5d2W11ecc4vRrQMQHXRSxzrCsyG5EsPPTbA==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.65.0.tgz", + "integrity": "sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg==", "dev": true, "license": "MIT", "engines": { @@ -21554,44 +21957,21 @@ } }, "node_modules/typescript-eslint/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.2.tgz", - "integrity": "sha512-2MKM+I6g8tJxfSmFKOnHv2t8Sk3T6rF20A1Puk0svLK+uVapDZB/4pfAeB7nE83uAZrU6OxW+HmOd5wHVdXwXA==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.65.0.tgz", + "integrity": "sha512-JboAE2swaYt4tb1fHhHTABE2K+OLy09XfcTbhnk4Pw96f9dd2e9iYsJ28gBggHlo5z5x1rkyWvcPoTuNTd4oGg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.57.2", - "@typescript-eslint/tsconfig-utils": "8.57.2", - "@typescript-eslint/types": "8.57.2", - "@typescript-eslint/visitor-keys": "8.57.2", + "@typescript-eslint/project-service": "8.65.0", + "@typescript-eslint/tsconfig-utils": "8.65.0", + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/visitor-keys": "8.65.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.4.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/utils": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.57.2.tgz", - "integrity": "sha512-krRIbvPK1ju1WBKIefiX+bngPs+odIQUtR7kymzPfo1POVw3jlF+nLkmexdSSd4UCbDcQn+wMBATOOmpBbqgKg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.57.2", - "@typescript-eslint/types": "8.57.2", - "@typescript-eslint/typescript-estree": "8.57.2" + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -21601,18 +21981,17 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/typescript-eslint/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.57.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.2.tgz", - "integrity": "sha512-zhahknjobV2FiD6Ee9iLbS7OV9zi10rG26odsQdfBO/hjSzUQbkIYgda+iNKK1zNiW2ey+Lf8MU5btN17V3dUw==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.65.0.tgz", + "integrity": "sha512-8C71BQkGjiMmXtop7pHVJu1l2NNShFdkCyD6a2ezzs5vU/L3LRtb69EtcteFwz0mYMPzIgOw0n6OV4VBUWZd7A==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.57.2", + "@typescript-eslint/types": "8.65.0", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -21634,16 +22013,16 @@ } }, "node_modules/typescript-eslint/node_modules/brace-expansion": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", - "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz", + "integrity": "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, "engines": { - "node": "18 || 20 || >=22" + "node": "20 || >=22" } }, "node_modules/typescript-eslint/node_modules/eslint-visitor-keys": { @@ -21659,24 +22038,14 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/typescript-eslint/node_modules/ignore": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, "node_modules/typescript-eslint/node_modules/minimatch": { - "version": "10.2.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", - "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^5.0.2" + "brace-expansion": "^5.0.8" }, "engines": { "node": "18 || 20 || >=22" @@ -21721,9 +22090,9 @@ } }, "node_modules/unbash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unbash/-/unbash-3.0.0.tgz", - "integrity": "sha512-FeFPZ/WFT0mbRCuydiZzpPFlrYN8ZUpphQKoq4EeElVIYjYyGzPMxQR/simUwCOJIyVhpFk4RbtyO7RuMpMnHA==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/unbash/-/unbash-4.0.4.tgz", + "integrity": "sha512-60m9IVGbavD6jholbxt0jVBXZkEB/HsMZq7Tyaghseve2/Sf0zQRAIfWsD34sde+DKP2tBxJS2wP88ZM0D1FhA==", "dev": true, "license": "ISC", "engines": { @@ -21758,6 +22127,12 @@ "node": ">=20.18.1" } }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "license": "MIT" + }, "node_modules/unhomoglyph": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/unhomoglyph/-/unhomoglyph-1.0.6.tgz", @@ -21772,6 +22147,44 @@ "node": ">= 0.8" } }, + "node_modules/unrs-resolver": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.12.2.tgz", + "integrity": "sha512-dmlRxBJJayXjqTwC+JtF1HhJmgf3ftQ3YejFcZrf4+KKtJv0qDsK1pjqaaVjG7wJ5NJ6UVP1OqRMQ71Z4C3rxQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "napi-postinstall": "^0.3.4" + }, + "funding": { + "url": "https://opencollective.com/unrs-resolver" + }, + "optionalDependencies": { + "@unrs/resolver-binding-android-arm-eabi": "1.12.2", + "@unrs/resolver-binding-android-arm64": "1.12.2", + "@unrs/resolver-binding-darwin-arm64": "1.12.2", + "@unrs/resolver-binding-darwin-x64": "1.12.2", + "@unrs/resolver-binding-freebsd-x64": "1.12.2", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.12.2", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.12.2", + "@unrs/resolver-binding-linux-arm64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-arm64-musl": "1.12.2", + "@unrs/resolver-binding-linux-loong64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-loong64-musl": "1.12.2", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-riscv64-musl": "1.12.2", + "@unrs/resolver-binding-linux-s390x-gnu": "1.12.2", + "@unrs/resolver-binding-linux-x64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-x64-musl": "1.12.2", + "@unrs/resolver-binding-openharmony-arm64": "1.12.2", + "@unrs/resolver-binding-wasm32-wasi": "1.12.2", + "@unrs/resolver-binding-win32-arm64-msvc": "1.12.2", + "@unrs/resolver-binding-win32-ia32-msvc": "1.12.2", + "@unrs/resolver-binding-win32-x64-msvc": "1.12.2" + } + }, "node_modules/untildify": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", diff --git a/server/package.json b/server/package.json index 90f1b97e..ad42a813 100644 --- a/server/package.json +++ b/server/package.json @@ -130,7 +130,7 @@ "eslint-plugin-node": "^11.1.0", "eslint-plugin-promise": "^7.2.1", "eslint-plugin-security": "^1.7.1", - "eslint-plugin-switch-statement": "^0.0.11", + "eslint-plugin-switch-statement": "^0.0.12", "jest": "^29.3.1", "jest-junit": "^16.0.0", "jest-light-runner": "^0.4.1", @@ -140,7 +140,7 @@ "supertest": "^6.2.2", "ts-node": "^10.9.1", "tsc-watch": "^4.6.0", - "typescript": "^5.5.2", + "typescript": "5.5.2", "typescript-eslint": "^8.57.2" }, "jest-junit": { From ec65bb2d9827b4e236244a60a8e965482cbfcac3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Jul 2026 13:46:31 +0100 Subject: [PATCH 42/57] build(deps-dev): bump the root-dev group across 1 directory with 3 updates (#942) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * build(deps-dev): bump the root-dev group across 1 directory with 3 updates Bumps the root-dev group with 3 updates in the / directory: [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node), [prettier](https://github.com/prettier/prettier) and [@parcel/watcher](https://github.com/parcel-bundler/watcher). Updates `@types/node` from 24.12.2 to 24.13.3 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) Updates `prettier` from 3.8.3 to 3.9.6 - [Release notes](https://github.com/prettier/prettier/releases) - [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md) - [Commits](https://github.com/prettier/prettier/compare/3.8.3...3.9.6) Updates `@parcel/watcher` from 2.5.6 to 2.6.0 - [Release notes](https://github.com/parcel-bundler/watcher/releases) - [Commits](https://github.com/parcel-bundler/watcher/compare/v2.5.6...v2.6.0) --- updated-dependencies: - dependency-name: "@parcel/watcher" dependency-version: 2.6.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: root-dev - dependency-name: "@types/node" dependency-version: 24.13.3 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: root-dev - dependency-name: prettier dependency-version: 3.9.5 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: root-dev ... Signed-off-by: dependabot[bot] * Format code with Prettier 3.9 Co-Authored-By: Codex --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Tao Bojlén Co-authored-by: Codex --- client/src/components/ItemAction.tsx | 6 +- client/src/coop-ui/Typography.tsx | 11 +- client/src/graphql/generated.ts | 85 ++++------ .../dashboard/banks/hash/HashBankForm.tsx | 4 +- .../bulk_actioning/BulkActioningDashboard.tsx | 3 +- .../dashboard/components/CoopButton.tsx | 6 +- .../dashboard/components/RoundedTag.tsx | 4 +- .../item_types/itemTypeCodeSampleUtils.ts | 7 +- .../ManualReviewJobReview.tsx | 6 +- .../v2/ncmec/NCMECReviewUser.tsx | 12 +- .../ManualReviewDashboardInsightsChart.tsx | 8 +- .../rules/rule_form/ReportingRuleForm.tsx | 4 +- .../dashboard/rules/rule_form/RuleForm.tsx | 8 +- client/src/webpages/dashboard/rules/types.ts | 3 +- client/src/webpages/settings/SettingsPage.tsx | 7 +- docs/api/policies.md | 10 +- package-lock.json | 152 ++++++++---------- package.json | 6 +- server/graphql/datasources/OrgApi.ts | 3 +- server/graphql/datasources/RuleApi.ts | 7 +- server/graphql/datasources/UserApi.ts | 16 +- .../datasources/buildGraphqlRuleParent.ts | 3 +- server/graphql/datasources/orgValidation.ts | 3 +- server/graphql/datasources/userValidation.ts | 10 +- server/graphql/generated.ts | 85 ++++------ server/graphql/modules/itemType.ts | 14 +- server/iocContainer/index.ts | 4 +- server/iocContainer/utils.ts | 3 +- server/lib/cache/types/utils.ts | 7 +- .../queries/IActionStatisticsAdapter.ts | 11 +- server/routes/items/submitItems.ts | 3 +- server/routes/reporting/ReportingRoutes.ts | 3 +- server/scylla/cqlUtils.ts | 3 +- server/scylla/types.ts | 3 +- .../ruleExecutionLoggingUtils.ts | 7 +- .../analyticsQueries/RuleActionInsights.ts | 3 +- .../itemInvestigationService.ts | 6 +- .../manualReviewToolService/dbTypes.ts | 11 +- .../manualReviewToolService.ts | 6 +- .../modules/JobDecisioning.ts | 3 +- .../modules/MatchingBankOperations.ts | 3 +- .../types/conditionResults.ts | 6 +- .../types/itemTypes.ts | 8 +- .../services/ncmecService/ncmecReporting.ts | 8 +- .../notificationsService.ts | 3 +- .../signalExecutionService.ts | 4 +- .../reportingService/ReportingRules.ts | 3 +- .../reportingService/reportingService.ts | 3 +- .../sendEmailService/sendEmailService.ts | 3 +- .../dataWarehouse/DataWarehouseFactory.ts | 3 +- .../storage/dataWarehouse/warehouseSchema.ts | 15 +- server/test/arbitraries/ContentType.ts | 24 +-- server/utils/apiKeyMiddleware.ts | 3 +- server/utils/json-schema-types.ts | 15 +- server/utils/kyselyTransactionWithRetry.ts | 3 +- server/utils/typescript-types.ts | 9 +- types/index.ts | 4 +- 57 files changed, 235 insertions(+), 438 deletions(-) diff --git a/client/src/components/ItemAction.tsx b/client/src/components/ItemAction.tsx index 5d1da1d6..1d3a2044 100644 --- a/client/src/components/ItemAction.tsx +++ b/client/src/components/ItemAction.tsx @@ -30,8 +30,7 @@ type EligibleAction = { }; type ParamsModalState = - | { open: false } - | { open: true; mode: 'create' | 'edit'; actionId: string }; + { open: false } | { open: true; mode: 'create' | 'edit'; actionId: string }; export default function ItemAction(props: { itemIdentifier: ItemIdentifier; @@ -187,8 +186,7 @@ export default function ItemAction(props: { // issue. See https://github.com/microsoft/TypeScript/issues/17002 for // more details. const policyId = policyIds satisfies - | string - | readonly string[] as string; + string | readonly string[] as string; setSelectedPolicyIds([policyId]); } }, diff --git a/client/src/coop-ui/Typography.tsx b/client/src/coop-ui/Typography.tsx index 6aaf08e4..044f5127 100644 --- a/client/src/coop-ui/Typography.tsx +++ b/client/src/coop-ui/Typography.tsx @@ -3,16 +3,7 @@ import { cva, VariantProps } from 'class-variance-authority'; import React from 'react'; type TextSize = - | 'XXS' - | 'XS' - | 'SM' - | 'base' - | 'LG' - | 'XL' - | '2XL' - | '3XL' - | '4XL' - | '5XL'; + 'XXS' | 'XS' | 'SM' | 'base' | 'LG' | 'XL' | '2XL' | '3XL' | '4XL' | '5XL'; type TextWeight = 'regular' | 'medium' | 'semibold' | 'bold'; diff --git a/client/src/graphql/generated.ts b/client/src/graphql/generated.ts index 8ff77a07..3117604f 100644 --- a/client/src/graphql/generated.ts +++ b/client/src/graphql/generated.ts @@ -231,8 +231,7 @@ export type GQLAddFavoriteRuleSuccessResponse = { }; export type GQLAddManualReviewJobCommentResponse = - | GQLAddManualReviewJobCommentSuccessResponse - | GQLNotFoundError; + GQLAddManualReviewJobCommentSuccessResponse | GQLNotFoundError; export type GQLAddManualReviewJobCommentSuccessResponse = { readonly __typename: 'AddManualReviewJobCommentSuccessResponse'; @@ -416,8 +415,7 @@ export type GQLChangePasswordInput = { }; export type GQLChangePasswordResponse = - | GQLChangePasswordError - | GQLChangePasswordSuccessResponse; + GQLChangePasswordError | GQLChangePasswordSuccessResponse; export type GQLChangePasswordSuccessResponse = { readonly __typename: 'ChangePasswordSuccessResponse'; @@ -526,8 +524,7 @@ export type GQLConditionSetWithResult = { }; export type GQLConditionWithResult = - | GQLConditionSetWithResult - | GQLLeafConditionWithResult; + GQLConditionSetWithResult | GQLLeafConditionWithResult; export type GQLContainer = { readonly __typename: 'Container'; @@ -763,8 +760,7 @@ export type GQLCreateContentRuleInput = { }; export type GQLCreateContentRuleResponse = - | GQLMutateContentRuleSuccessResponse - | GQLRuleNameExistsError; + GQLMutateContentRuleSuccessResponse | GQLRuleNameExistsError; export type GQLCreateHashBankInput = { readonly description?: InputMaybe; @@ -813,8 +809,7 @@ export type GQLCreateReportingRuleInput = { }; export type GQLCreateReportingRuleResponse = - | GQLMutateReportingRuleSuccessResponse - | GQLReportingRuleNameExistsError; + GQLMutateReportingRuleSuccessResponse | GQLReportingRuleNameExistsError; export type GQLCreateRoutingRuleInput = { readonly conditionSet: GQLConditionSetInput; @@ -872,8 +867,7 @@ export type GQLCreateUserRuleInput = { }; export type GQLCreateUserRuleResponse = - | GQLMutateUserRuleSuccessResponse - | GQLRuleNameExistsError; + GQLMutateUserRuleSuccessResponse | GQLRuleNameExistsError; export type GQLCustomAction = GQLActionBase & { readonly __typename: 'CustomAction'; @@ -991,8 +985,7 @@ export const GQLDecisionsCountGroupBy = { export type GQLDecisionsCountGroupBy = (typeof GQLDecisionsCountGroupBy)[keyof typeof GQLDecisionsCountGroupBy]; export type GQLDeleteAllJobsFromQueueResponse = - | GQLDeleteAllJobsFromQueueSuccessResponse - | GQLDeleteAllJobsUnauthorizedError; + GQLDeleteAllJobsFromQueueSuccessResponse | GQLDeleteAllJobsUnauthorizedError; export type GQLDeleteAllJobsFromQueueSuccessResponse = { readonly __typename: 'DeleteAllJobsFromQueueSuccessResponse'; @@ -1010,8 +1003,7 @@ export type GQLDeleteAllJobsUnauthorizedError = GQLError & { }; export type GQLDeleteItemTypeResponse = - | GQLCannotDeleteDefaultUserError - | GQLDeleteItemTypeSuccessResponse; + GQLCannotDeleteDefaultUserError | GQLDeleteItemTypeSuccessResponse; export type GQLDeleteItemTypeSuccessResponse = { readonly __typename: 'DeleteItemTypeSuccessResponse'; @@ -1325,8 +1317,7 @@ export type GQLGetDecisionCountsTableInput = { }; export type GQLGetFullReportingRuleResultForItemResponse = - | GQLNotFoundError - | GQLReportingRuleExecutionResult; + GQLNotFoundError | GQLReportingRuleExecutionResult; export type GQLGetFullResultForItemInput = { readonly date?: InputMaybe; @@ -1336,8 +1327,7 @@ export type GQLGetFullResultForItemInput = { }; export type GQLGetFullResultForItemResponse = - | GQLNotFoundError - | GQLRuleExecutionResult; + GQLNotFoundError | GQLRuleExecutionResult; export type GQLGetJobCreationCountInput = { readonly filterBy: GQLJobCreationFilterByInput; @@ -1629,9 +1619,7 @@ export type GQLItemSubmissions = { }; export type GQLItemType = - | GQLContentItemType - | GQLThreadItemType - | GQLUserItemType; + GQLContentItemType | GQLThreadItemType | GQLUserItemType; export type GQLItemTypeBase = { readonly baseFields: ReadonlyArray; @@ -2118,8 +2106,7 @@ export const GQLManualReviewChartMetric = { export type GQLManualReviewChartMetric = (typeof GQLManualReviewChartMetric)[keyof typeof GQLManualReviewChartMetric]; export type GQLManualReviewChartSettings = - | GQLGetDecisionCountSettings - | GQLGetJobCreationCountSettings; + GQLGetDecisionCountSettings | GQLGetJobCreationCountSettings; export type GQLManualReviewChartSettingsInput = { readonly decisionCountSettings?: InputMaybe; @@ -2381,8 +2368,7 @@ export const GQLMutateActionError = { export type GQLMutateActionError = (typeof GQLMutateActionError)[keyof typeof GQLMutateActionError]; export type GQLMutateActionResponse = - | GQLActionNameExistsError - | GQLMutateActionSuccessResponse; + GQLActionNameExistsError | GQLMutateActionSuccessResponse; export type GQLMutateActionSuccessResponse = { readonly __typename: 'MutateActionSuccessResponse'; @@ -2396,8 +2382,7 @@ export type GQLMutateBankResponse = { }; export type GQLMutateContentItemTypeResponse = - | GQLItemTypeNameAlreadyExistsError - | GQLMutateContentTypeSuccessResponse; + GQLItemTypeNameAlreadyExistsError | GQLMutateContentTypeSuccessResponse; export type GQLMutateContentRuleSuccessResponse = { readonly __typename: 'MutateContentRuleSuccessResponse'; @@ -2410,8 +2395,7 @@ export type GQLMutateContentTypeSuccessResponse = { }; export type GQLMutateHashBankResponse = - | GQLMatchingBankNameExistsError - | GQLMutateHashBankSuccessResponse; + GQLMatchingBankNameExistsError | GQLMutateHashBankSuccessResponse; export type GQLMutateHashBankSuccessResponse = { readonly __typename: 'MutateHashBankSuccessResponse'; @@ -2420,8 +2404,7 @@ export type GQLMutateHashBankSuccessResponse = { }; export type GQLMutateLocationBankResponse = - | GQLLocationBankNameExistsError - | GQLMutateLocationBankSuccessResponse; + GQLLocationBankNameExistsError | GQLMutateLocationBankSuccessResponse; export type GQLMutateLocationBankSuccessResponse = { readonly __typename: 'MutateLocationBankSuccessResponse'; @@ -2449,8 +2432,7 @@ export type GQLMutateRoutingRulesOrderSuccessResponse = { }; export type GQLMutateThreadItemTypeResponse = - | GQLItemTypeNameAlreadyExistsError - | GQLMutateThreadTypeSuccessResponse; + GQLItemTypeNameAlreadyExistsError | GQLMutateThreadTypeSuccessResponse; export type GQLMutateThreadTypeSuccessResponse = { readonly __typename: 'MutateThreadTypeSuccessResponse'; @@ -2458,8 +2440,7 @@ export type GQLMutateThreadTypeSuccessResponse = { }; export type GQLMutateUserItemTypeResponse = - | GQLItemTypeNameAlreadyExistsError - | GQLMutateUserTypeSuccessResponse; + GQLItemTypeNameAlreadyExistsError | GQLMutateUserTypeSuccessResponse; export type GQLMutateUserRuleSuccessResponse = { readonly __typename: 'MutateUserRuleSuccessResponse'; @@ -3860,8 +3841,7 @@ export type GQLRemoveAccessibleQueuesToUserInput = { }; export type GQLRemoveAccessibleQueuesToUserResponse = - | GQLMutateAccessibleQueuesForUserSuccessResponse - | GQLNotFoundError; + GQLMutateAccessibleQueuesForUserSuccessResponse | GQLNotFoundError; export type GQLRemoveFavoriteMrtQueueSuccessResponse = { readonly __typename: 'RemoveFavoriteMRTQueueSuccessResponse'; @@ -4054,8 +4034,7 @@ export type GQLRotateApiKeyInput = { }; export type GQLRotateApiKeyResponse = - | GQLRotateApiKeyError - | GQLRotateApiKeySuccessResponse; + GQLRotateApiKeyError | GQLRotateApiKeySuccessResponse; export type GQLRotateApiKeySuccessResponse = { readonly __typename: 'RotateApiKeySuccessResponse'; @@ -4074,8 +4053,7 @@ export type GQLRotateWebhookSigningKeyError = GQLError & { }; export type GQLRotateWebhookSigningKeyResponse = - | GQLRotateWebhookSigningKeyError - | GQLRotateWebhookSigningKeySuccessResponse; + GQLRotateWebhookSigningKeyError | GQLRotateWebhookSigningKeySuccessResponse; export type GQLRotateWebhookSigningKeySuccessResponse = { readonly __typename: 'RotateWebhookSigningKeySuccessResponse'; @@ -4346,8 +4324,7 @@ export type GQLSignUpInput = { }; export type GQLSignUpResponse = - | GQLSignUpSuccessResponse - | GQLSignUpUserExistsError; + GQLSignUpSuccessResponse | GQLSignUpUserExistsError; export type GQLSignUpSuccessResponse = { readonly __typename: 'SignUpSuccessResponse'; @@ -4425,8 +4402,7 @@ export const GQLSignalInputType = { export type GQLSignalInputType = (typeof GQLSignalInputType)[keyof typeof GQLSignalInputType]; export type GQLSignalOutputType = - | GQLEnumSignalOutputType - | GQLScalarSignalOutputType; + GQLEnumSignalOutputType | GQLScalarSignalOutputType; export type GQLSignalPricingStructure = { readonly __typename: 'SignalPricingStructure'; @@ -7469,8 +7445,7 @@ export type GQLGetItemsByIpAddressQuery = { export type GQLGetAuthorInfoQueryVariables = Exact<{ userIdentifiers: - | ReadonlyArray - | GQLItemIdentifierInput; + ReadonlyArray | GQLItemIdentifierInput; }>; export type GQLGetAuthorInfoQuery = { @@ -16701,8 +16676,7 @@ export type GQLJobFieldsFragment = { export type GQLGetRelatedItemsQueryVariables = Exact<{ itemIdentifiers: - | ReadonlyArray - | GQLItemIdentifierInput; + ReadonlyArray | GQLItemIdentifierInput; }>; export type GQLGetRelatedItemsQuery = { @@ -16958,8 +16932,7 @@ export type GQLAllManualReviewQueuesQuery = { export type GQLGetLatestUserSubmittedItemsWithThreadsQueryVariables = Exact<{ userId: GQLItemIdentifierInput; reportedMessages: - | ReadonlyArray - | GQLItemIdentifierInput; + ReadonlyArray | GQLItemIdentifierInput; }>; export type GQLGetLatestUserSubmittedItemsWithThreadsQuery = { @@ -17486,8 +17459,7 @@ export type GQLGetMoreInfoForItemsQuery = { export type GQLGetUserItemsQueryVariables = Exact<{ itemIdentifiers: - | ReadonlyArray - | GQLItemIdentifierInput; + ReadonlyArray | GQLItemIdentifierInput; }>; export type GQLGetUserItemsQuery = { @@ -28281,8 +28253,7 @@ export function useGQLActionQuery( GQLActionQueryVariables > & ( - | { variables: GQLActionQueryVariables; skip?: boolean } - | { skip: boolean } + { variables: GQLActionQueryVariables; skip?: boolean } | { skip: boolean } ), ) { const options = { ...defaultOptions, ...baseOptions }; diff --git a/client/src/webpages/dashboard/banks/hash/HashBankForm.tsx b/client/src/webpages/dashboard/banks/hash/HashBankForm.tsx index faa639e6..b61f9525 100644 --- a/client/src/webpages/dashboard/banks/hash/HashBankForm.tsx +++ b/client/src/webpages/dashboard/banks/hash/HashBankForm.tsx @@ -23,9 +23,7 @@ import { } from '../../../../graphql/generated'; type SchemaField = GQLExchangeApiSchemaQuery['exchangeApiSchema'] extends - | infer S - | null - | undefined + infer S | null | undefined ? S extends { config_schema: { fields: ReadonlyArray } } ? F : never diff --git a/client/src/webpages/dashboard/bulk_actioning/BulkActioningDashboard.tsx b/client/src/webpages/dashboard/bulk_actioning/BulkActioningDashboard.tsx index 4d6dcca3..d2b32017 100644 --- a/client/src/webpages/dashboard/bulk_actioning/BulkActioningDashboard.tsx +++ b/client/src/webpages/dashboard/bulk_actioning/BulkActioningDashboard.tsx @@ -314,8 +314,7 @@ export default function BulkActioningDashboard() { // issue. See https://github.com/microsoft/TypeScript/issues/17002 for // more details. const policyId = policyIds satisfies - | string - | readonly string[] as string; + string | readonly string[] as string; setSelectedPolicyIds([policyId]); } }} diff --git a/client/src/webpages/dashboard/components/CoopButton.tsx b/client/src/webpages/dashboard/components/CoopButton.tsx index ec6c3455..d46f6044 100644 --- a/client/src/webpages/dashboard/components/CoopButton.tsx +++ b/client/src/webpages/dashboard/components/CoopButton.tsx @@ -6,11 +6,7 @@ import { Link } from 'react-router-dom'; export type CoopButtonSize = 'small' | 'middle' | 'large'; export type CoopButtonType = - | 'primary' - | 'secondary' - | 'danger' - | 'green' - | 'link'; + 'primary' | 'secondary' | 'danger' | 'green' | 'link'; export type CoopButtonFontWeight = 'normal' | 'semibold'; export type CoopButtonIconStyle = 'fill' | 'stroke'; diff --git a/client/src/webpages/dashboard/components/RoundedTag.tsx b/client/src/webpages/dashboard/components/RoundedTag.tsx index 9b2cbe22..81c90db5 100644 --- a/client/src/webpages/dashboard/components/RoundedTag.tsx +++ b/client/src/webpages/dashboard/components/RoundedTag.tsx @@ -4,9 +4,7 @@ export default function RoundedTag( props: { title: string; } & ( - | { status: GQLRuleStatus } - | { environment: GQLRuleEnvironment } - | object + { status: GQLRuleStatus } | { environment: GQLRuleEnvironment } | object ), ) { const { title } = props; diff --git a/client/src/webpages/dashboard/item_types/itemTypeCodeSampleUtils.ts b/client/src/webpages/dashboard/item_types/itemTypeCodeSampleUtils.ts index 283f410b..e7f8153c 100644 --- a/client/src/webpages/dashboard/item_types/itemTypeCodeSampleUtils.ts +++ b/client/src/webpages/dashboard/item_types/itemTypeCodeSampleUtils.ts @@ -114,12 +114,7 @@ export function generateItemData( } type JsonValue = - | string - | number - | boolean - | null - | JsonValue[] - | { [k: string]: JsonValue }; + string | number | boolean | null | JsonValue[] | { [k: string]: JsonValue }; export function translateJSONObjectToPHP(json: JsonValue): string { function translateValue(value: JsonValue, indentLevel: number = 0): string { diff --git a/client/src/webpages/dashboard/mrt/manual_review_job/ManualReviewJobReview.tsx b/client/src/webpages/dashboard/mrt/manual_review_job/ManualReviewJobReview.tsx index 7f528b20..0fce0b7f 100644 --- a/client/src/webpages/dashboard/mrt/manual_review_job/ManualReviewJobReview.tsx +++ b/client/src/webpages/dashboard/mrt/manual_review_job/ManualReviewJobReview.tsx @@ -1402,8 +1402,7 @@ function ManualReviewJobReviewImpl(props: { // issue. See https://github.com/microsoft/TypeScript/issues/17002 for // more details. const policyId = policyIds satisfies - | string - | readonly string[] as string; + string | readonly string[] as string; setSelectedPrimaryPolicies(policiesFromIds([policyId])); setSelectedPrimaryActions( selectedPrimaryActions.map((action) => ({ @@ -1458,8 +1457,7 @@ function ManualReviewJobReviewImpl(props: { - it.contentItem.id === mediaInDetailView.itemId && - it.urlInfo.url === mediaInDetailView.urlInfo.url, - )! - } + fullNcmecContentItem={allMediaItemsWithUrls.find( + (it) => + it.contentItem.id === mediaInDetailView.itemId && + it.urlInfo.url === mediaInDetailView.urlInfo.url, + )!} state={selectedMedia.find((it) => areMediaEqual(it, mediaInDetailView), )} diff --git a/client/src/webpages/dashboard/mrt/visualization/ManualReviewDashboardInsightsChart.tsx b/client/src/webpages/dashboard/mrt/visualization/ManualReviewDashboardInsightsChart.tsx index df19679d..be9d7511 100644 --- a/client/src/webpages/dashboard/mrt/visualization/ManualReviewDashboardInsightsChart.tsx +++ b/client/src/webpages/dashboard/mrt/visualization/ManualReviewDashboardInsightsChart.tsx @@ -177,10 +177,7 @@ gql` `; export type ManualReviewDashboardInsightsChartMetric = - | 'DECISIONS' - | 'JOBS' - | 'REVIEWED_JOBS' - | 'SKIPPED_JOBS'; + 'DECISIONS' | 'JOBS' | 'REVIEWED_JOBS' | 'SKIPPED_JOBS'; export function getEmptyFilterState( metric: ManualReviewDashboardInsightsChartMetric, @@ -223,8 +220,7 @@ export default function ManualReviewDashboardInsightsChart(props: { timeWindow: TimeWindow; initialChartType: ChartType; initialGroupBy: - | Array - | undefined; + Array | undefined; metric: ManualReviewDashboardInsightsChartMetric; title?: string; isCustomTitle?: boolean; diff --git a/client/src/webpages/dashboard/rules/rule_form/ReportingRuleForm.tsx b/client/src/webpages/dashboard/rules/rule_form/ReportingRuleForm.tsx index 9e0cd632..858e0709 100644 --- a/client/src/webpages/dashboard/rules/rule_form/ReportingRuleForm.tsx +++ b/client/src/webpages/dashboard/rules/rule_form/ReportingRuleForm.tsx @@ -476,8 +476,8 @@ export default function RuleForm() { dispatch({ type: ReportingRuleFormReducerActionType.UpdateItemTypes, payload: { - selectedItemTypes: selectedTypeIDs.map( - (id) => allItemTypes.find((itemType) => itemType.id === id)!, + selectedItemTypes: selectedTypeIDs.map((id) => + allItemTypes.find((itemType) => itemType.id === id)!, ), allActions, allSignals: allSignals satisfies readonly GQLSignal[], diff --git a/client/src/webpages/dashboard/rules/rule_form/RuleForm.tsx b/client/src/webpages/dashboard/rules/rule_form/RuleForm.tsx index e577412c..d752b346 100644 --- a/client/src/webpages/dashboard/rules/rule_form/RuleForm.tsx +++ b/client/src/webpages/dashboard/rules/rule_form/RuleForm.tsx @@ -823,9 +823,7 @@ export default function RuleForm() { const showUpdateRuleCaughtErrorModal = ( isUpdate: boolean, errorName: - | 'NotFoundError' - | 'RuleNameExistsError' - | 'RuleHasRunningBacktestsError', + 'NotFoundError' | 'RuleNameExistsError' | 'RuleHasRunningBacktestsError', ) => { dispatch({ type: RuleFormReducerActionType.ShowModal, @@ -1113,8 +1111,8 @@ export default function RuleForm() { dispatch({ type: RuleFormReducerActionType.UpdateItemTypes, payload: { - selectedItemTypes: selectedTypeIDs.map( - (id: string) => allItemTypes.find((itemType) => itemType.id === id)!, + selectedItemTypes: selectedTypeIDs.map((id: string) => + allItemTypes.find((itemType) => itemType.id === id)!, ), allActions, allSignals: allSignals satisfies readonly GQLSignal[], diff --git a/client/src/webpages/dashboard/rules/types.ts b/client/src/webpages/dashboard/rules/types.ts index bc230904..5e056286 100644 --- a/client/src/webpages/dashboard/rules/types.ts +++ b/client/src/webpages/dashboard/rules/types.ts @@ -59,8 +59,7 @@ export function getMatchingValuesType(matchingValues: GQLMatchingValues) { } export type ConditionWithResult = - | LeafConditionWithResult - | ConditionSetWithResult; + LeafConditionWithResult | ConditionSetWithResult; export type ConditionSetWithResult = { conditions: [ConditionWithResult, ...ConditionWithResult[]]; diff --git a/client/src/webpages/settings/SettingsPage.tsx b/client/src/webpages/settings/SettingsPage.tsx index 4a2b956d..318ee6c0 100644 --- a/client/src/webpages/settings/SettingsPage.tsx +++ b/client/src/webpages/settings/SettingsPage.tsx @@ -79,12 +79,7 @@ gql` `; type Tab = - | 'organization' - | 'sso' - | 'appeals' - | 'review-console' - | 'wellness' - | 'other'; + 'organization' | 'sso' | 'appeals' | 'review-console' | 'wellness' | 'other'; const TABS: { value: Tab; label: string; icon: React.ReactNode }[] = [ { diff --git a/docs/api/policies.md b/docs/api/policies.md index 67f8b43e..85b33a8d 100644 --- a/docs/api/policies.md +++ b/docs/api/policies.md @@ -29,12 +29,12 @@ Authentication: `X-API-KEY` header. See [API Keys & Authentication](../developme ### Response fields -| Field | Type | Description | +| Field | Type | Description | | :-------------------- | :----- | :------------------------------------------- | ---------------------------------------------------------------- | -| `policies` | Array | All policies for your organization | -| `policies[].id` | String | Coop's unique, immutable ID for this policy | -| `policies[].name` | String | The display name you assigned to this policy | -| `policies[].parentId` | String | null | ID of the parent policy, or `null` if this is a top-level policy | +| `policies` | Array | All policies for your organization | +| `policies[].id` | String | Coop's unique, immutable ID for this policy | +| `policies[].name` | String | The display name you assigned to this policy | +| `policies[].parentId` | String | null | ID of the parent policy, or `null` if this is a top-level policy | ## Notes diff --git a/package-lock.json b/package-lock.json index 751b3328..992042da 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,16 +17,16 @@ "@graphql-codegen/typescript-react-apollo": "^4.4.2", "@graphql-codegen/typescript-resolvers": "^5.1.8", "@ianvs/prettier-plugin-sort-imports": "^4.7.1", - "@parcel/watcher": "^2.5.6", + "@parcel/watcher": "^2.6.0", "@types/express": "^5.0.6", "@types/jest": "^30.0.0", - "@types/node": "^24.0.0", + "@types/node": "^24.13.3", "@types/passport": "^1.0.17", "@types/validator": "^13.15.10", "concurrently": "^10.0.3", "husky": "^9.1.7", "lint-staged": "^16.4.0", - "prettier": "^3.8.3", + "prettier": "^3.9.6", "ts-node": "^10.9.2", "typescript": "^5.9.0" }, @@ -1858,9 +1858,9 @@ } }, "node_modules/@parcel/watcher": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.6.tgz", - "integrity": "sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.6.0.tgz", + "integrity": "sha512-7FNeNl8NCE7aINx7WXiKQrPYZWC/hvrTsmk6zmxbI7LTXE7hVek/n8AfVgpe2y82zl3w0HvCHN0bVKMBoJcC0w==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -1868,7 +1868,7 @@ "detect-libc": "^2.0.3", "is-glob": "^4.0.3", "node-addon-api": "^7.0.0", - "picomatch": "^4.0.3" + "picomatch": "^4.0.4" }, "engines": { "node": ">= 10.0.0" @@ -1878,25 +1878,24 @@ "url": "https://opencollective.com/parcel" }, "optionalDependencies": { - "@parcel/watcher-android-arm64": "2.5.6", - "@parcel/watcher-darwin-arm64": "2.5.6", - "@parcel/watcher-darwin-x64": "2.5.6", - "@parcel/watcher-freebsd-x64": "2.5.6", - "@parcel/watcher-linux-arm-glibc": "2.5.6", - "@parcel/watcher-linux-arm-musl": "2.5.6", - "@parcel/watcher-linux-arm64-glibc": "2.5.6", - "@parcel/watcher-linux-arm64-musl": "2.5.6", - "@parcel/watcher-linux-x64-glibc": "2.5.6", - "@parcel/watcher-linux-x64-musl": "2.5.6", - "@parcel/watcher-win32-arm64": "2.5.6", - "@parcel/watcher-win32-ia32": "2.5.6", - "@parcel/watcher-win32-x64": "2.5.6" + "@parcel/watcher-android-arm64": "2.6.0", + "@parcel/watcher-darwin-arm64": "2.6.0", + "@parcel/watcher-darwin-x64": "2.6.0", + "@parcel/watcher-freebsd-x64": "2.6.0", + "@parcel/watcher-linux-arm-glibc": "2.6.0", + "@parcel/watcher-linux-arm-musl": "2.6.0", + "@parcel/watcher-linux-arm64-glibc": "2.6.0", + "@parcel/watcher-linux-arm64-musl": "2.6.0", + "@parcel/watcher-linux-x64-glibc": "2.6.0", + "@parcel/watcher-linux-x64-musl": "2.6.0", + "@parcel/watcher-win32-arm64": "2.6.0", + "@parcel/watcher-win32-x64": "2.6.0" } }, "node_modules/@parcel/watcher-android-arm64": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.6.tgz", - "integrity": "sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.6.0.tgz", + "integrity": "sha512-trgpLSCKRC/huFjXX/Smh+0sWe4+YtKfktIToiMl59ghz7z+qkH6kMvNnUbLyRs9N11t8l4svSCs1+5B3rOAhA==", "cpu": [ "arm64" ], @@ -1915,9 +1914,9 @@ } }, "node_modules/@parcel/watcher-darwin-arm64": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.6.tgz", - "integrity": "sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.6.0.tgz", + "integrity": "sha512-Y3QV0gl7Q1zbfueunkWIERICbEojQFCgpyG7YqOGNFLsckXyI1xu9mAIUpKY9QBYzBtSkN8dBPwd3yiAO9ovMw==", "cpu": [ "arm64" ], @@ -1936,9 +1935,9 @@ } }, "node_modules/@parcel/watcher-darwin-x64": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.6.tgz", - "integrity": "sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.6.0.tgz", + "integrity": "sha512-Ohv6OpzhUfKYD7Beb8kDvG0jbIxORCYY1JRdZnaBtnjjkJxgD7ZVL0nw2sCYd0yTMKTvz3nnTnOF3cDifK+kvw==", "cpu": [ "x64" ], @@ -1957,9 +1956,9 @@ } }, "node_modules/@parcel/watcher-freebsd-x64": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.6.tgz", - "integrity": "sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.6.0.tgz", + "integrity": "sha512-5HmXvDgs8VK+74jF9y9/2FE3/OnlcKmc56tjmSrEuZjpSZOGL+fvAu+HKJBdPs9uwoP2hE6TlSUpXZ/C5jUFmQ==", "cpu": [ "x64" ], @@ -1978,9 +1977,9 @@ } }, "node_modules/@parcel/watcher-linux-arm-glibc": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.6.tgz", - "integrity": "sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.6.0.tgz", + "integrity": "sha512-Ps/hui3A+vMbjdqlqAowK2ZL8+BO8dBjxeWXj6npTBs3jx4wWmbPpaLuqwrQrSqIVMCnpWo238bJ1U37GhQOYg==", "cpu": [ "arm" ], @@ -2002,9 +2001,9 @@ } }, "node_modules/@parcel/watcher-linux-arm-musl": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.6.tgz", - "integrity": "sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.6.0.tgz", + "integrity": "sha512-9c6AUHgHoG+IY88MRIHupztQiQnrbqHYQjkM2btA+Bf/wQnQMuiD0Wfk1EVv3TlNT3x41uU71rn6E4xh/+zvkw==", "cpu": [ "arm" ], @@ -2026,9 +2025,9 @@ } }, "node_modules/@parcel/watcher-linux-arm64-glibc": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.6.tgz", - "integrity": "sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.6.0.tgz", + "integrity": "sha512-yHRqS2owEXe6Hic9z6Mh1ECsCd+ODVOGvZDyciqRd21+v+o+DnXMOrw50DSpIG2sb8GPEaPPmfeCAWKPJdq46g==", "cpu": [ "arm64" ], @@ -2050,9 +2049,9 @@ } }, "node_modules/@parcel/watcher-linux-arm64-musl": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.6.tgz", - "integrity": "sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.6.0.tgz", + "integrity": "sha512-WhB2e/V7rqdHHWZusBSPuy5Ei8S6lSz6FE5TKKQz5h3a0O+C+mhY7vxU9b/stqvMb8beLnPY82ZrFTLKs+SrKA==", "cpu": [ "arm64" ], @@ -2074,9 +2073,9 @@ } }, "node_modules/@parcel/watcher-linux-x64-glibc": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.6.tgz", - "integrity": "sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.6.0.tgz", + "integrity": "sha512-ulGE6x6Oz6iAwg75T8YQSoguBWasniIbX+QWpaYPcCnDOpdWX3k+4xbEYPZVLxOuoJI+svJJPD3sEj8G7lrQ3A==", "cpu": [ "x64" ], @@ -2098,9 +2097,9 @@ } }, "node_modules/@parcel/watcher-linux-x64-musl": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.6.tgz", - "integrity": "sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.6.0.tgz", + "integrity": "sha512-tkBYKt7YQrjIJWYDnto2YgO8MRkjlMTSNoRHzsXinBqbLdeOM3L32wPZJvIZxqaLMfSlS/4sUjH/6STVP/XDLw==", "cpu": [ "x64" ], @@ -2122,9 +2121,9 @@ } }, "node_modules/@parcel/watcher-win32-arm64": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.6.tgz", - "integrity": "sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.6.0.tgz", + "integrity": "sha512-gIZAP23jaHjGWasY/TY6yL7NHFClf0Ga7FN+iINvk+KN94rhm94lYZhFsbYFNcA04/onvGD9kKmiJLJB2HbNwQ==", "cpu": [ "arm64" ], @@ -2142,31 +2141,10 @@ "url": "https://opencollective.com/parcel" } }, - "node_modules/@parcel/watcher-win32-ia32": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.6.tgz", - "integrity": "sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, "node_modules/@parcel/watcher-win32-x64": { - "version": "2.5.6", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.6.tgz", - "integrity": "sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.6.0.tgz", + "integrity": "sha512-cA+/pXV2YkfxlIcXOQ5fSWqAzzPyD78/x5qbK/I0vUkrlYHA8TIz+MXjAbGouguKVSI4bOmkTSJ1/poVSsgt+A==", "cpu": [ "x64" ], @@ -2319,13 +2297,13 @@ } }, "node_modules/@types/node": { - "version": "24.12.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.2.tgz", - "integrity": "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g==", + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.3.tgz", + "integrity": "sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~7.16.0" + "undici-types": "~7.18.0" } }, "node_modules/@types/passport": { @@ -4723,9 +4701,9 @@ } }, "node_modules/prettier": { - "version": "3.8.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.3.tgz", - "integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==", + "version": "3.9.6", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.6.tgz", + "integrity": "sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==", "dev": true, "license": "MIT", "bin": { @@ -5181,9 +5159,9 @@ } }, "node_modules/undici-types": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", - "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", "dev": true, "license": "MIT" }, diff --git a/package.json b/package.json index 37d43fb5..ef32517b 100644 --- a/package.json +++ b/package.json @@ -41,13 +41,13 @@ "lint-staged": "^16.4.0", "@types/express": "^5.0.6", "@types/jest": "^30.0.0", - "@types/node": "^24.0.0", + "@types/node": "^24.13.3", "@types/passport": "^1.0.17", "@types/validator": "^13.15.10", "concurrently": "^10.0.3", "husky": "^9.1.7", - "prettier": "^3.8.3", - "@parcel/watcher": "^2.5.6", + "prettier": "^3.9.6", + "@parcel/watcher": "^2.6.0", "ts-node": "^10.9.2", "typescript": "^5.9.0" } diff --git a/server/graphql/datasources/OrgApi.ts b/server/graphql/datasources/OrgApi.ts index ce9a1876..d4e0bdb0 100644 --- a/server/graphql/datasources/OrgApi.ts +++ b/server/graphql/datasources/OrgApi.ts @@ -195,8 +195,7 @@ class OrgAPI { } export type OrgErrorType = - | 'InviteUserTokenExpiredError' - | 'InviteUserTokenMissingError'; + 'InviteUserTokenExpiredError' | 'InviteUserTokenMissingError'; function orgValidationFailureToBadRequestError(failure: OrgValidationFailure) { return makeBadRequestError(failure.message, { diff --git a/server/graphql/datasources/RuleApi.ts b/server/graphql/datasources/RuleApi.ts index a80cf958..a80e88b5 100644 --- a/server/graphql/datasources/RuleApi.ts +++ b/server/graphql/datasources/RuleApi.ts @@ -380,9 +380,7 @@ class RuleAPI { orgId: string, actionIds: readonly string[], actionParameters: - | readonly { actionId: string; parameters: unknown }[] - | null - | undefined, + readonly { actionId: string; parameters: unknown }[] | null | undefined, ): Promise | undefined> { if (actionIds.length === 0) { return undefined; @@ -990,8 +988,7 @@ function conditionInputIsValid( } type ValidatedGQLConditionInput = - | ValidatedGQLConditionSetInput - | ValidatedGQLLeafConditionInput; + ValidatedGQLConditionSetInput | ValidatedGQLLeafConditionInput; type ValidatedGQLConditionSetInput = RequiredWithoutNull< Pick diff --git a/server/graphql/datasources/UserApi.ts b/server/graphql/datasources/UserApi.ts index 3bd00b73..80b5cf2b 100644 --- a/server/graphql/datasources/UserApi.ts +++ b/server/graphql/datasources/UserApi.ts @@ -159,15 +159,13 @@ class UserAPI { token: inviteUserToken, }); } - if ( - !( - token != null && - token.email === email && - token.orgId === orgId && - token.role === role && - Date.now() - new Date(token.createdAt).getTime() < 2 * WEEK_MS - ) - ) { + if (!( + token != null && + token.email === email && + token.orgId === orgId && + token.role === role && + Date.now() - new Date(token.createdAt).getTime() < 2 * WEEK_MS + )) { throw makeUnauthorizedError('Invalid invite token', { shouldErrorSpan: true, }); diff --git a/server/graphql/datasources/buildGraphqlRuleParent.ts b/server/graphql/datasources/buildGraphqlRuleParent.ts index 6107dc90..844510f9 100644 --- a/server/graphql/datasources/buildGraphqlRuleParent.ts +++ b/server/graphql/datasources/buildGraphqlRuleParent.ts @@ -26,8 +26,7 @@ export function buildGraphqlRuleParent( // getActions and getActionParameters resolve from the same joined read, so // share one lazy promise to avoid querying the rule's actions twice. let actionsWithParameters: - | ReturnType - | undefined; + ReturnType | undefined; const getActionsWithParameters = async () => { actionsWithParameters ??= deps.moderationConfigService.getActionsForRuleId({ orgId: plain.orgId, diff --git a/server/graphql/datasources/orgValidation.ts b/server/graphql/datasources/orgValidation.ts index 8e1cd036..2b3d6140 100644 --- a/server/graphql/datasources/orgValidation.ts +++ b/server/graphql/datasources/orgValidation.ts @@ -31,8 +31,7 @@ export type OrgValidationFailure = { }; export type OrgValidationResult = - | { ok: true } - | { ok: false; failure: OrgValidationFailure }; + { ok: true } | { ok: false; failure: OrgValidationFailure }; function isEmailShape(value: string): boolean { return validator.isEmail(value); diff --git a/server/graphql/datasources/userValidation.ts b/server/graphql/datasources/userValidation.ts index 92cc2fd4..71493034 100644 --- a/server/graphql/datasources/userValidation.ts +++ b/server/graphql/datasources/userValidation.ts @@ -26,18 +26,12 @@ const validator = createRequire(import.meta.url)('validator') as ValidatorLib; export type UserValidationFailure = { /** Kept stable for GraphQL JSON pointers. */ field: - | 'email' - | 'firstName' - | 'lastName' - | 'role' - | 'loginMethods' - | 'password'; + 'email' | 'firstName' | 'lastName' | 'role' | 'loginMethods' | 'password'; message: string; }; export type UserValidationResult = - | { ok: true } - | { ok: false; failure: UserValidationFailure }; + { ok: true } | { ok: false; failure: UserValidationFailure }; function isEmailShape(value: string): boolean { return validator.isEmail(value); diff --git a/server/graphql/generated.ts b/server/graphql/generated.ts index 8430dcfd..178d0f82 100644 --- a/server/graphql/generated.ts +++ b/server/graphql/generated.ts @@ -299,8 +299,7 @@ export type GQLAddFavoriteRuleSuccessResponse = { }; export type GQLAddManualReviewJobCommentResponse = - | GQLAddManualReviewJobCommentSuccessResponse - | GQLNotFoundError; + GQLAddManualReviewJobCommentSuccessResponse | GQLNotFoundError; export type GQLAddManualReviewJobCommentSuccessResponse = { readonly __typename?: 'AddManualReviewJobCommentSuccessResponse'; @@ -484,8 +483,7 @@ export type GQLChangePasswordInput = { }; export type GQLChangePasswordResponse = - | GQLChangePasswordError - | GQLChangePasswordSuccessResponse; + GQLChangePasswordError | GQLChangePasswordSuccessResponse; export type GQLChangePasswordSuccessResponse = { readonly __typename?: 'ChangePasswordSuccessResponse'; @@ -594,8 +592,7 @@ export type GQLConditionSetWithResult = { }; export type GQLConditionWithResult = - | GQLConditionSetWithResult - | GQLLeafConditionWithResult; + GQLConditionSetWithResult | GQLLeafConditionWithResult; export type GQLContainer = { readonly __typename?: 'Container'; @@ -831,8 +828,7 @@ export type GQLCreateContentRuleInput = { }; export type GQLCreateContentRuleResponse = - | GQLMutateContentRuleSuccessResponse - | GQLRuleNameExistsError; + GQLMutateContentRuleSuccessResponse | GQLRuleNameExistsError; export type GQLCreateHashBankInput = { readonly description?: InputMaybe; @@ -881,8 +877,7 @@ export type GQLCreateReportingRuleInput = { }; export type GQLCreateReportingRuleResponse = - | GQLMutateReportingRuleSuccessResponse - | GQLReportingRuleNameExistsError; + GQLMutateReportingRuleSuccessResponse | GQLReportingRuleNameExistsError; export type GQLCreateRoutingRuleInput = { readonly conditionSet: GQLConditionSetInput; @@ -940,8 +935,7 @@ export type GQLCreateUserRuleInput = { }; export type GQLCreateUserRuleResponse = - | GQLMutateUserRuleSuccessResponse - | GQLRuleNameExistsError; + GQLMutateUserRuleSuccessResponse | GQLRuleNameExistsError; export type GQLCustomAction = GQLActionBase & { readonly __typename?: 'CustomAction'; @@ -1059,8 +1053,7 @@ export const GQLDecisionsCountGroupBy = { export type GQLDecisionsCountGroupBy = (typeof GQLDecisionsCountGroupBy)[keyof typeof GQLDecisionsCountGroupBy]; export type GQLDeleteAllJobsFromQueueResponse = - | GQLDeleteAllJobsFromQueueSuccessResponse - | GQLDeleteAllJobsUnauthorizedError; + GQLDeleteAllJobsFromQueueSuccessResponse | GQLDeleteAllJobsUnauthorizedError; export type GQLDeleteAllJobsFromQueueSuccessResponse = { readonly __typename?: 'DeleteAllJobsFromQueueSuccessResponse'; @@ -1078,8 +1071,7 @@ export type GQLDeleteAllJobsUnauthorizedError = GQLError & { }; export type GQLDeleteItemTypeResponse = - | GQLCannotDeleteDefaultUserError - | GQLDeleteItemTypeSuccessResponse; + GQLCannotDeleteDefaultUserError | GQLDeleteItemTypeSuccessResponse; export type GQLDeleteItemTypeSuccessResponse = { readonly __typename?: 'DeleteItemTypeSuccessResponse'; @@ -1393,8 +1385,7 @@ export type GQLGetDecisionCountsTableInput = { }; export type GQLGetFullReportingRuleResultForItemResponse = - | GQLNotFoundError - | GQLReportingRuleExecutionResult; + GQLNotFoundError | GQLReportingRuleExecutionResult; export type GQLGetFullResultForItemInput = { readonly date?: InputMaybe; @@ -1404,8 +1395,7 @@ export type GQLGetFullResultForItemInput = { }; export type GQLGetFullResultForItemResponse = - | GQLNotFoundError - | GQLRuleExecutionResult; + GQLNotFoundError | GQLRuleExecutionResult; export type GQLGetJobCreationCountInput = { readonly filterBy: GQLJobCreationFilterByInput; @@ -1697,9 +1687,7 @@ export type GQLItemSubmissions = { }; export type GQLItemType = - | GQLContentItemType - | GQLThreadItemType - | GQLUserItemType; + GQLContentItemType | GQLThreadItemType | GQLUserItemType; export type GQLItemTypeBase = { readonly baseFields: ReadonlyArray; @@ -2186,8 +2174,7 @@ export const GQLManualReviewChartMetric = { export type GQLManualReviewChartMetric = (typeof GQLManualReviewChartMetric)[keyof typeof GQLManualReviewChartMetric]; export type GQLManualReviewChartSettings = - | GQLGetDecisionCountSettings - | GQLGetJobCreationCountSettings; + GQLGetDecisionCountSettings | GQLGetJobCreationCountSettings; export type GQLManualReviewChartSettingsInput = { readonly decisionCountSettings?: InputMaybe; @@ -2449,8 +2436,7 @@ export const GQLMutateActionError = { export type GQLMutateActionError = (typeof GQLMutateActionError)[keyof typeof GQLMutateActionError]; export type GQLMutateActionResponse = - | GQLActionNameExistsError - | GQLMutateActionSuccessResponse; + GQLActionNameExistsError | GQLMutateActionSuccessResponse; export type GQLMutateActionSuccessResponse = { readonly __typename?: 'MutateActionSuccessResponse'; @@ -2464,8 +2450,7 @@ export type GQLMutateBankResponse = { }; export type GQLMutateContentItemTypeResponse = - | GQLItemTypeNameAlreadyExistsError - | GQLMutateContentTypeSuccessResponse; + GQLItemTypeNameAlreadyExistsError | GQLMutateContentTypeSuccessResponse; export type GQLMutateContentRuleSuccessResponse = { readonly __typename?: 'MutateContentRuleSuccessResponse'; @@ -2478,8 +2463,7 @@ export type GQLMutateContentTypeSuccessResponse = { }; export type GQLMutateHashBankResponse = - | GQLMatchingBankNameExistsError - | GQLMutateHashBankSuccessResponse; + GQLMatchingBankNameExistsError | GQLMutateHashBankSuccessResponse; export type GQLMutateHashBankSuccessResponse = { readonly __typename?: 'MutateHashBankSuccessResponse'; @@ -2488,8 +2472,7 @@ export type GQLMutateHashBankSuccessResponse = { }; export type GQLMutateLocationBankResponse = - | GQLLocationBankNameExistsError - | GQLMutateLocationBankSuccessResponse; + GQLLocationBankNameExistsError | GQLMutateLocationBankSuccessResponse; export type GQLMutateLocationBankSuccessResponse = { readonly __typename?: 'MutateLocationBankSuccessResponse'; @@ -2517,8 +2500,7 @@ export type GQLMutateRoutingRulesOrderSuccessResponse = { }; export type GQLMutateThreadItemTypeResponse = - | GQLItemTypeNameAlreadyExistsError - | GQLMutateThreadTypeSuccessResponse; + GQLItemTypeNameAlreadyExistsError | GQLMutateThreadTypeSuccessResponse; export type GQLMutateThreadTypeSuccessResponse = { readonly __typename?: 'MutateThreadTypeSuccessResponse'; @@ -2526,8 +2508,7 @@ export type GQLMutateThreadTypeSuccessResponse = { }; export type GQLMutateUserItemTypeResponse = - | GQLItemTypeNameAlreadyExistsError - | GQLMutateUserTypeSuccessResponse; + GQLItemTypeNameAlreadyExistsError | GQLMutateUserTypeSuccessResponse; export type GQLMutateUserRuleSuccessResponse = { readonly __typename?: 'MutateUserRuleSuccessResponse'; @@ -3928,8 +3909,7 @@ export type GQLRemoveAccessibleQueuesToUserInput = { }; export type GQLRemoveAccessibleQueuesToUserResponse = - | GQLMutateAccessibleQueuesForUserSuccessResponse - | GQLNotFoundError; + GQLMutateAccessibleQueuesForUserSuccessResponse | GQLNotFoundError; export type GQLRemoveFavoriteMrtQueueSuccessResponse = { readonly __typename?: 'RemoveFavoriteMRTQueueSuccessResponse'; @@ -4122,8 +4102,7 @@ export type GQLRotateApiKeyInput = { }; export type GQLRotateApiKeyResponse = - | GQLRotateApiKeyError - | GQLRotateApiKeySuccessResponse; + GQLRotateApiKeyError | GQLRotateApiKeySuccessResponse; export type GQLRotateApiKeySuccessResponse = { readonly __typename?: 'RotateApiKeySuccessResponse'; @@ -4142,8 +4121,7 @@ export type GQLRotateWebhookSigningKeyError = GQLError & { }; export type GQLRotateWebhookSigningKeyResponse = - | GQLRotateWebhookSigningKeyError - | GQLRotateWebhookSigningKeySuccessResponse; + GQLRotateWebhookSigningKeyError | GQLRotateWebhookSigningKeySuccessResponse; export type GQLRotateWebhookSigningKeySuccessResponse = { readonly __typename?: 'RotateWebhookSigningKeySuccessResponse'; @@ -4414,8 +4392,7 @@ export type GQLSignUpInput = { }; export type GQLSignUpResponse = - | GQLSignUpSuccessResponse - | GQLSignUpUserExistsError; + GQLSignUpSuccessResponse | GQLSignUpUserExistsError; export type GQLSignUpSuccessResponse = { readonly __typename?: 'SignUpSuccessResponse'; @@ -4493,8 +4470,7 @@ export const GQLSignalInputType = { export type GQLSignalInputType = (typeof GQLSignalInputType)[keyof typeof GQLSignalInputType]; export type GQLSignalOutputType = - | GQLEnumSignalOutputType - | GQLScalarSignalOutputType; + GQLEnumSignalOutputType | GQLScalarSignalOutputType; export type GQLSignalPricingStructure = { readonly __typename?: 'SignalPricingStructure'; @@ -5473,8 +5449,7 @@ export type GQLResolversUnionTypes<_RefType extends Record> = { }) | GQLNotFoundError; ChangePasswordResponse: - | GQLChangePasswordError - | GQLChangePasswordSuccessResponse; + GQLChangePasswordError | GQLChangePasswordSuccessResponse; Condition: ConditionSet | LeafCondition; ConditionWithResult: ConditionSetWithResult | LeafConditionWithResult; CreateContentRuleResponse: @@ -5507,8 +5482,7 @@ export type GQLResolversUnionTypes<_RefType extends Record> = { | GQLDeleteAllJobsFromQueueSuccessResponse | GQLDeleteAllJobsUnauthorizedError; DeleteItemTypeResponse: - | GQLCannotDeleteDefaultUserError - | GQLDeleteItemTypeSuccessResponse; + GQLCannotDeleteDefaultUserError | GQLDeleteItemTypeSuccessResponse; DequeueManualReviewJobResponse: Omit< GQLDequeueManualReviewJobSuccessResponse, 'job' @@ -5563,8 +5537,7 @@ export type GQLResolversUnionTypes<_RefType extends Record> = { | (Omit & { user: _RefType['User'] }) | GQLLoginUserDoesNotExistError; ManualReviewChartSettings: - | GQLGetDecisionCountSettings - | GQLGetJobCreationCountSettings; + GQLGetDecisionCountSettings | GQLGetJobCreationCountSettings; ManualReviewDecisionComponent: | GQLAcceptAppealDecisionComponent | GQLAutomaticCloseDecisionComponent @@ -5627,8 +5600,7 @@ export type GQLResolversUnionTypes<_RefType extends Record> = { items: ReadonlyArray<_RefType['Item']>; }); RemoveAccessibleQueuesToUserResponse: - | GQLMutateAccessibleQueuesForUserSuccessResponse - | GQLNotFoundError; + GQLMutateAccessibleQueuesForUserSuccessResponse | GQLNotFoundError; RemoveFavoriteRuleResponse: GQLRemoveFavoriteRuleSuccessResponse; ReorderRoutingRulesResponse: Omit< GQLMutateRoutingRulesOrderSuccessResponse, @@ -5636,8 +5608,7 @@ export type GQLResolversUnionTypes<_RefType extends Record> = { > & { data: ReadonlyArray<_RefType['RoutingRule']> }; RotateApiKeyResponse: GQLRotateApiKeyError | GQLRotateApiKeySuccessResponse; RotateWebhookSigningKeyResponse: - | GQLRotateWebhookSigningKeyError - | GQLRotateWebhookSigningKeySuccessResponse; + GQLRotateWebhookSigningKeyError | GQLRotateWebhookSigningKeySuccessResponse; RunRetroactionResponse: GQLRunRetroactionSuccessResponse; SchemaFieldRoles: | GQLContentSchemaFieldRoles diff --git a/server/graphql/modules/itemType.ts b/server/graphql/modules/itemType.ts index 021c52df..f7994a62 100644 --- a/server/graphql/modules/itemType.ts +++ b/server/graphql/modules/itemType.ts @@ -40,12 +40,10 @@ import { gqlErrorResult, gqlSuccessResult } from '../utils/gqlResult.js'; export type ItemTypeResolversParentType = ItemTypeT | ItemTypeSelector; export type ThreadItemTypeResolversParentType = - | ThreadItemTypeT - | ItemTypeSelector; + ThreadItemTypeT | ItemTypeSelector; export type UserItemTypeResolversParentType = UserItemTypeT | ItemTypeSelector; export type ContentItemTypeResolversParentType = - | ContentItemTypeT - | ItemTypeSelector; + ContentItemTypeT | ItemTypeSelector; const typeDefs = /* GraphQL */ ` interface Field { @@ -1082,13 +1080,9 @@ function isValidField(it: GQLFieldInput): it is Field { // similar idea to the `satisfies` checks above. const containerType = it.container?.containerType satisfies - | ContainerType - | null - | undefined; + ContainerType | null | undefined; const keyScalarType = it.container?.keyScalarType satisfies - | ScalarType - | null - | undefined; + ScalarType | null | undefined; const isValidContainerType = containerTypes.includes(type) && diff --git a/server/iocContainer/index.ts b/server/iocContainer/index.ts index 91549183..05218b48 100644 --- a/server/iocContainer/index.ts +++ b/server/iocContainer/index.ts @@ -1703,7 +1703,9 @@ export default async function getBottle() { Dependencies, { [ServiceName in keyof Dependencies]: { - [Method in CloseMethodName]: Dependencies[ServiceName] extends { + [ + Method in CloseMethodName + ]: Dependencies[ServiceName] extends { [_ in Method]: unknown; } ? ServiceName diff --git a/server/iocContainer/utils.ts b/server/iocContainer/utils.ts index bc301aa4..db5baf20 100644 --- a/server/iocContainer/utils.ts +++ b/server/iocContainer/utils.ts @@ -10,8 +10,7 @@ const DEPENDENCIES = Symbol(); type DepName = keyof Deps; export type Factory = - | ((...args: D) => R) - | (new (...args: D) => R); + ((...args: D) => R) | (new (...args: D) => R); export type AnnotatedFactory = Factory & { [DEPENDENCIES]: DepName[]; diff --git a/server/lib/cache/types/utils.ts b/server/lib/cache/types/utils.ts index afffa3f8..90d8cec4 100644 --- a/server/lib/cache/types/utils.ts +++ b/server/lib/cache/types/utils.ts @@ -2,12 +2,7 @@ * JSON-serializable values. */ export type JSON = - | null - | string - | boolean - | number - | JSON[] - | { [k: string]: JSON }; + null | string | boolean | number | JSON[] | { [k: string]: JSON }; export type Bind1< F extends (arg0: A0, ...args: never[]) => unknown, diff --git a/server/plugins/warehouse/queries/IActionStatisticsAdapter.ts b/server/plugins/warehouse/queries/IActionStatisticsAdapter.ts index 1d358c3a..9bbd85a2 100644 --- a/server/plugins/warehouse/queries/IActionStatisticsAdapter.ts +++ b/server/plugins/warehouse/queries/IActionStatisticsAdapter.ts @@ -3,17 +3,10 @@ import { type ReadonlyDeep } from 'type-fest'; export type ActionStatisticsTimeDivisionOptions = 'DAY' | 'HOUR'; export type ActionExecutionsGroupByAllowedFields = - | 'RULE_ID' - | 'ACTION_ID' - | 'ITEM_TYPE_ID' - | 'ACTION_SOURCE' - | 'POLICY_ID'; + 'RULE_ID' | 'ACTION_ID' | 'ITEM_TYPE_ID' | 'ACTION_SOURCE' | 'POLICY_ID'; export type ActionSourceOptions = - | 'automated-rule' - | 'mrt-decision' - | 'manual-action-run' - | 'post-actions'; + 'automated-rule' | 'mrt-decision' | 'manual-action-run' | 'post-actions'; export type ActionCountsInput = ReadonlyDeep<{ orgId: string; diff --git a/server/routes/items/submitItems.ts b/server/routes/items/submitItems.ts index c02e3a04..33ac96dd 100644 --- a/server/routes/items/submitItems.ts +++ b/server/routes/items/submitItems.ts @@ -109,8 +109,7 @@ Dependencies): RequestHandlerWithBodies { ) { try { const images = itemSubmission.itemSubmission.data.images as ( - | string - | { url: string; [key: string]: unknown } + string | { url: string; [key: string]: unknown } )[]; // Get all hash banks for this org once diff --git a/server/routes/reporting/ReportingRoutes.ts b/server/routes/reporting/ReportingRoutes.ts index 56bac598..252d1bfd 100644 --- a/server/routes/reporting/ReportingRoutes.ts +++ b/server/routes/reporting/ReportingRoutes.ts @@ -12,8 +12,7 @@ import submitReport from './submitReport.js'; export type ReportItemInput = { reporter: - | { kind: 'rule'; id: string } - | { kind: 'user'; typeId: string; id: string }; + { kind: 'rule'; id: string } | { kind: 'user'; typeId: string; id: string }; reportedAt: string; reportedForReason?: { policyId?: string | null; diff --git a/server/scylla/cqlUtils.ts b/server/scylla/cqlUtils.ts index b9a39a89..52f77cc1 100644 --- a/server/scylla/cqlUtils.ts +++ b/server/scylla/cqlUtils.ts @@ -23,8 +23,7 @@ type SelectClause = readonly [ ]; type Selector = - | Cols - | { aggregate: 'count' | 'sum' | 'avg' | 'min' | 'max'; col: Cols }; + Cols | { aggregate: 'count' | 'sum' | 'avg' | 'min' | 'max'; col: Cols }; export type CqlSelectOptions< DBRelations extends DBDefinition, diff --git a/server/scylla/types.ts b/server/scylla/types.ts index cda43ab7..1398cd26 100644 --- a/server/scylla/types.ts +++ b/server/scylla/types.ts @@ -28,8 +28,7 @@ export type ScyllaNilItemIdentifier = typeof ScyllaNilItemIdentifier; export const ScyllaNilItemIdentifier = { id: '', type_id: '' } as const; export type ScyllaItemIdentifier = - | ScyllaRealItemIdentifier - | ScyllaNilItemIdentifier; + ScyllaRealItemIdentifier | ScyllaNilItemIdentifier; export function isRealItemIdentifier( it: ScyllaItemIdentifier, diff --git a/server/services/analyticsLoggers/ruleExecutionLoggingUtils.ts b/server/services/analyticsLoggers/ruleExecutionLoggingUtils.ts index cc350cec..9b4b0855 100644 --- a/server/services/analyticsLoggers/ruleExecutionLoggingUtils.ts +++ b/server/services/analyticsLoggers/ruleExecutionLoggingUtils.ts @@ -43,8 +43,7 @@ export type ConditionSetWithResultAsLogged = Omit< }; export type ConditionWithResultAsLogged = - | ConditionSetWithResultAsLogged - | LeafConditionWithResultAsLogged; + ConditionSetWithResultAsLogged | LeafConditionWithResultAsLogged; // NB: we make these types to ensure, at the type level, that we're generating // correlation ids consistently for everything we log. @@ -162,8 +161,8 @@ export function pickLeafConditionPropsTolog( threshold: condition.threshold, input: condition.input, result: condition.result satisfies - | ReadonlyDeep - | undefined as ConditionResultAsLogged | undefined, + ReadonlyDeep | undefined as + ConditionResultAsLogged | undefined, signal: signal && { id: signal.id, type: signal.type, diff --git a/server/services/analyticsQueries/RuleActionInsights.ts b/server/services/analyticsQueries/RuleActionInsights.ts index 988a5c33..4e468847 100644 --- a/server/services/analyticsQueries/RuleActionInsights.ts +++ b/server/services/analyticsQueries/RuleActionInsights.ts @@ -477,8 +477,7 @@ export default inject( export { type RuleActionInsights, type SignalWithScore }; type GatherSignalsConditionWithResult = - | GatherSignalsConditionSetWithResult - | GatherSignalsLeafConditionWithResult; + GatherSignalsConditionSetWithResult | GatherSignalsLeafConditionWithResult; type GatherSignalsLeafConditionWithResult = { signal?: { diff --git a/server/services/itemInvestigationService/itemInvestigationService.ts b/server/services/itemInvestigationService/itemInvestigationService.ts index 3b8f9177..7b7ccce6 100644 --- a/server/services/itemInvestigationService/itemInvestigationService.ts +++ b/server/services/itemInvestigationService/itemInvestigationService.ts @@ -573,8 +573,7 @@ export class ItemInvestigationService { const submissionId = record.submissionId as SubmissionId; const itemData = record.itemData as JsonOf; const schemaVariant = record.itemTypeSchemaVariant as - | 'original' - | 'partial'; + 'original' | 'partial'; return dbRowToItemSubmissionWithItemTypeIdentifier({ submission_id: submissionId, @@ -1092,8 +1091,7 @@ export class ItemInvestigationService { item_data: record.itemData as JsonOf, item_submission_time: record.occurredAt, item_type_schema_variant: record.itemTypeSchemaVariant as - | 'original' - | 'partial', + 'original' | 'partial', }); for (const group of recordsByItem.values()) { diff --git a/server/services/manualReviewToolService/dbTypes.ts b/server/services/manualReviewToolService/dbTypes.ts index 3bccd11d..e5d73a2a 100644 --- a/server/services/manualReviewToolService/dbTypes.ts +++ b/server/services/manualReviewToolService/dbTypes.ts @@ -34,9 +34,7 @@ import { type RoutingRuleStatus } from './modules/JobRouting.js'; // What to do with a user's other pending reports when a trigger action is // taken on one of their jobs (issue #650). export type ClearReportsDisposition = - | 'AUTOMATIC_CLOSE' - | 'IGNORE' - | 'SAME_ACTION'; + 'AUTOMATIC_CLOSE' | 'IGNORE' | 'SAME_ACTION'; export type ClearReportsScope = 'CURRENT_QUEUE' | 'ALL_QUEUES'; @@ -106,9 +104,7 @@ export type ManualReviewToolServicePg = { decision_components: ManualReviewDecisionComponent[]; related_actions: ManualReviewDecisionRelatedAction[]; enqueue_source_info: - | ManualReviewJobEnqueueSourceInfo - | AppealEnqueueSourceInfo - | null; + ManualReviewJobEnqueueSourceInfo | AppealEnqueueSourceInfo | null; item_created_at: Date | null; decision_reason: string | null; }; @@ -200,8 +196,7 @@ export type ManualReviewToolServicePg = { item_type_id: string; created_at: Date; enqueue_source_info: - | ManualReviewJobEnqueueSourceInfo - | AppealEnqueueSourceInfo; + ManualReviewJobEnqueueSourceInfo | AppealEnqueueSourceInfo; policy_ids: string[]; }; 'manual_review_tool.flattened_job_creations': { diff --git a/server/services/manualReviewToolService/manualReviewToolService.ts b/server/services/manualReviewToolService/manualReviewToolService.ts index 2f09e0aa..ad6f2e48 100644 --- a/server/services/manualReviewToolService/manualReviewToolService.ts +++ b/server/services/manualReviewToolService/manualReviewToolService.ts @@ -262,11 +262,7 @@ export type ManualReviewJobPayload = | NcmecManualReviewJobPayload; export type ManualReviewJobEnqueueSource = - | 'APPEAL' - | 'REPORT' - | 'RULE_EXECUTION' - | 'MRT_JOB' - | 'POST_ACTIONS'; + 'APPEAL' | 'REPORT' | 'RULE_EXECUTION' | 'MRT_JOB' | 'POST_ACTIONS'; export type RuleExecutionEnqueueSourceInfo = { kind: 'RULE_EXECUTION'; diff --git a/server/services/manualReviewToolService/modules/JobDecisioning.ts b/server/services/manualReviewToolService/modules/JobDecisioning.ts index 1081ce70..65298085 100644 --- a/server/services/manualReviewToolService/modules/JobDecisioning.ts +++ b/server/services/manualReviewToolService/modules/JobDecisioning.ts @@ -135,8 +135,7 @@ export type ManualReviewDecisionComponent = }; export type ManualReviewDecisionType = - | ManualReviewDecisionComponent['type'] - | 'RELATED_ACTION'; + ManualReviewDecisionComponent['type'] | 'RELATED_ACTION'; export type CustomActionDecisionComponent = Extract< ManualReviewDecisionComponent, diff --git a/server/services/moderationConfigService/modules/MatchingBankOperations.ts b/server/services/moderationConfigService/modules/MatchingBankOperations.ts index e1481f1f..2b4180f6 100644 --- a/server/services/moderationConfigService/modules/MatchingBankOperations.ts +++ b/server/services/moderationConfigService/modules/MatchingBankOperations.ts @@ -155,8 +155,7 @@ export default class MatchingBankOperations { } export type MatchingBankErrorType = - | 'MatchingBankNameExistsError' - | 'MatchingBankNotFoundError'; + 'MatchingBankNameExistsError' | 'MatchingBankNotFoundError'; export const makeMatchingBankNameExistsError = (data: ErrorInstanceData) => new CoopError({ diff --git a/server/services/moderationConfigService/types/conditionResults.ts b/server/services/moderationConfigService/types/conditionResults.ts index 4cf5f470..cbcdc541 100644 --- a/server/services/moderationConfigService/types/conditionResults.ts +++ b/server/services/moderationConfigService/types/conditionResults.ts @@ -21,8 +21,7 @@ export enum ConditionFailureOutcome { } export type ConditionOutcome = - | ConditionCompletionOutcome - | ConditionFailureOutcome; + ConditionCompletionOutcome | ConditionFailureOutcome; export type ConditionCompletionMetadata = { score?: string; @@ -49,8 +48,7 @@ export type ConditionResult = & ConditionResultCommonMetadata) export type ConditionWithResult = - | LeafConditionWithResult - | ConditionSetWithResult; + LeafConditionWithResult | ConditionSetWithResult; export type ConditionSetWithResult = Omit & { conditions: diff --git a/server/services/moderationConfigService/types/itemTypes.ts b/server/services/moderationConfigService/types/itemTypes.ts index c38c4e99..9e77179f 100644 --- a/server/services/moderationConfigService/types/itemTypes.ts +++ b/server/services/moderationConfigService/types/itemTypes.ts @@ -15,9 +15,7 @@ export type ItemTypeSchemaVariant = 'original' | 'partial'; export { ItemTypeKind }; export type ItemType = - | Readonly - | Readonly - | Readonly; + Readonly | Readonly | Readonly; type ItemTypeBase = { id: string; @@ -84,9 +82,7 @@ export type ContentSchemaFieldRoles = { ); export type SchemaFieldRoles = - | UserSchemaFieldRoles - | ThreadSchemaFieldRoles - | ContentSchemaFieldRoles; + UserSchemaFieldRoles | ThreadSchemaFieldRoles | ContentSchemaFieldRoles; /** * These three fields uniquely identify a particular "incarnation" of a given diff --git a/server/services/ncmecService/ncmecReporting.ts b/server/services/ncmecService/ncmecReporting.ts index 79e9a297..a660b816 100644 --- a/server/services/ncmecService/ncmecReporting.ts +++ b/server/services/ncmecService/ncmecReporting.ts @@ -1348,10 +1348,7 @@ export type NcmecMessagesReport = { }; type NcmecReportResult = - | 'ALL_MEDIA_MISSING' - | 'SUCCESS' - | 'UNSUPPORTED_ORG' - | 'FAILURE'; + 'ALL_MEDIA_MISSING' | 'SUCCESS' | 'UNSUPPORTED_ORG' | 'FAILURE'; const actionsOnReportCreationAndPoliciesSelection = [ 'actions_to_run_upon_report_creation as actionsToRunIds', @@ -1394,8 +1391,7 @@ export default class NcmecReporting { .where('org_id', '=', orgId) .executeTakeFirst(); return row as - | NcmecReportingServicePg['ncmec_reporting.ncmec_org_settings'] - | undefined; + NcmecReportingServicePg['ncmec_reporting.ncmec_org_settings'] | undefined; } async getNCMECActionsToRunAndPolicies( diff --git a/server/services/notificationsService/notificationsService.ts b/server/services/notificationsService/notificationsService.ts index a8f0999f..d221ac6e 100644 --- a/server/services/notificationsService/notificationsService.ts +++ b/server/services/notificationsService/notificationsService.ts @@ -61,8 +61,7 @@ export type Notification = { * specifies a delivery channel. Consider emails like oncall-sre@example.com */ type Recipient = - | { type: 'user_id'; value: string } - | { type: 'email_address'; value: string }; + { type: 'user_id'; value: string } | { type: 'email_address'; value: string }; type CreateNotificationInput = Pick< Notification, diff --git a/server/services/orgAwareSignalExecutionService/signalExecutionService.ts b/server/services/orgAwareSignalExecutionService/signalExecutionService.ts index 4872a13f..c5c5141b 100644 --- a/server/services/orgAwareSignalExecutionService/signalExecutionService.ts +++ b/server/services/orgAwareSignalExecutionService/signalExecutionService.ts @@ -200,9 +200,7 @@ async function runSignal( matchingValues?.strings ?? matchingValues?.locations ?? []; let matchingValuesFromBanks: readonly ( - | string - | ReadonlyDeep - | HashBank + string | ReadonlyDeep | HashBank )[] = []; if (textBankIds?.length) { matchingValuesFromBanks = await textBanksStringsLoader( diff --git a/server/services/reportingService/ReportingRules.ts b/server/services/reportingService/ReportingRules.ts index 951afc05..b4f398af 100644 --- a/server/services/reportingService/ReportingRules.ts +++ b/server/services/reportingService/ReportingRules.ts @@ -461,8 +461,7 @@ export default class ReportingRules { } export type ReportingRuleErrorType = - | 'ReportingRuleNameExistsError' - | 'NotFoundError'; + 'ReportingRuleNameExistsError' | 'NotFoundError'; function isReportingRuleNameExistsError(error: unknown) { return ( diff --git a/server/services/reportingService/reportingService.ts b/server/services/reportingService/reportingService.ts index c672fcff..a2108d57 100644 --- a/server/services/reportingService/reportingService.ts +++ b/server/services/reportingService/reportingService.ts @@ -34,8 +34,7 @@ import ReportingRules, { export type ReporterKind = 'rule' | 'user'; export type Reporter = - | { kind: 'rule'; id: string } - | { kind: 'user'; typeId: string; id: string }; + { kind: 'rule'; id: string } | { kind: 'user'; typeId: string; id: string }; export type Appealer = { typeId: string; id: string }; diff --git a/server/services/sendEmailService/sendEmailService.ts b/server/services/sendEmailService/sendEmailService.ts index d880fa30..764896ad 100644 --- a/server/services/sendEmailService/sendEmailService.ts +++ b/server/services/sendEmailService/sendEmailService.ts @@ -11,8 +11,7 @@ export type CoopEmailAddress = (typeof CoopEmailAddress)[keyof typeof CoopEmailAddress]; type Content = - | { text: string; html?: string } - | { html: string; text?: string }; + { text: string; html?: string } | { html: string; text?: string }; export type Message = Content & { to: string | string[]; diff --git a/server/storage/dataWarehouse/DataWarehouseFactory.ts b/server/storage/dataWarehouse/DataWarehouseFactory.ts index fd2799a2..5f85fd1b 100644 --- a/server/storage/dataWarehouse/DataWarehouseFactory.ts +++ b/server/storage/dataWarehouse/DataWarehouseFactory.ts @@ -325,8 +325,7 @@ export class DataWarehouseFactory { password: process.env.CLICKHOUSE_PASSWORD ?? '', database: process.env.CLICKHOUSE_DATABASE ?? 'default', protocol: (process.env.CLICKHOUSE_PROTOCOL ?? 'http') as - | 'http' - | 'https', + 'http' | 'https', }, pool: { max: process.env.CLICKHOUSE_POOL_SIZE diff --git a/server/storage/dataWarehouse/warehouseSchema.ts b/server/storage/dataWarehouse/warehouseSchema.ts index edc88b1e..023bbc61 100644 --- a/server/storage/dataWarehouse/warehouseSchema.ts +++ b/server/storage/dataWarehouse/warehouseSchema.ts @@ -43,7 +43,9 @@ export type { // Each table is registered both as `PUBLIC.TABLE_NAME` and just `TABLE_NAME`, // so queries can reference it with or without the schema prefix. export type DataWarehousePublicSchema = UnprefixedPublicTables & { - [K in keyof UnprefixedPublicTables as `PUBLIC.${K}`]: UnprefixedPublicTables[K]; + [ + K in keyof UnprefixedPublicTables as `PUBLIC.${K}` + ]: UnprefixedPublicTables[K]; }; /** Convert a warehouse driver date to a standard Date object. */ @@ -109,14 +111,9 @@ export type UnsafeBulkWriteType< | ManualReviewToolServiceWarehouseSchema[keyof ManualReviewToolServiceWarehouseSchema] | ReportingServiceWarehouseSchema[keyof ReportingServiceWarehouseSchema], > = { - [K in Exclude< - keyof T & string, - NullableKeysOf - > as Lowercase]: T[K] extends ColumnType< - unknown, - infer InsertType, - unknown - > + [ + K in Exclude> as Lowercase + ]: T[K] extends ColumnType ? InsertType : T[K]; } & { diff --git a/server/test/arbitraries/ContentType.ts b/server/test/arbitraries/ContentType.ts index 70fb29b7..7f78774d 100644 --- a/server/test/arbitraries/ContentType.ts +++ b/server/test/arbitraries/ContentType.ts @@ -162,18 +162,18 @@ export const MapFieldArbitrary = fc fc.string(), fc.boolean(), ) - .map< - Field - >(([valueScalarType, keyScalarType, name, required]) => ({ - name, - required, - type: ContainerTypes.MAP, - container: { - containerType: ContainerTypes.MAP, - keyScalarType, - valueScalarType, - }, - })); + .map>( + ([valueScalarType, keyScalarType, name, required]) => ({ + name, + required, + type: ContainerTypes.MAP, + container: { + containerType: ContainerTypes.MAP, + keyScalarType, + valueScalarType, + }, + }), + ); export const MapFieldWithValueArbitrary = MapFieldArbitrary.chain((field) => fc.tuple( diff --git a/server/utils/apiKeyMiddleware.ts b/server/utils/apiKeyMiddleware.ts index 20a96ee2..17d27fc7 100644 --- a/server/utils/apiKeyMiddleware.ts +++ b/server/utils/apiKeyMiddleware.ts @@ -20,8 +20,7 @@ export interface RequestWithOrgId { export function createApiKeyMiddleware< ReqBody extends JsonObject = JsonObject, ResBody extends ReadonlyDeep | undefined = - | ReadonlyDeep - | undefined, + ReadonlyDeep | undefined, >({ ApiKeyService, }: Pick): RequestHandlerWithBodies< diff --git a/server/utils/json-schema-types.ts b/server/utils/json-schema-types.ts index b195fb2b..7a1b7841 100644 --- a/server/utils/json-schema-types.ts +++ b/server/utils/json-schema-types.ts @@ -43,7 +43,7 @@ interface StringKeywords { type UncheckedJSONSchemaType = ( | // these two unions allow arbitrary unions of types - { + { anyOf: readonly UncheckedJSONSchemaType[]; } | { @@ -113,8 +113,7 @@ type UncheckedJSONSchemaType = ( // "patternProperties" and can be only used with interfaces that have string index type: JSONType<'object', IsPartial>; additionalProperties?: - | boolean - | UncheckedJSONSchemaType; + boolean | UncheckedJSONSchemaType; properties?: IsPartial extends true ? Partial> : PropertiesSchema; @@ -151,17 +150,11 @@ type UncheckedJSONSchemaType = ( export type JSONSchemaV4 = UncheckedJSONSchemaType; export type JSON = - | { [key: string]: JSON } - | JSON[] - | number - | string - | boolean - | null; + { [key: string]: JSON } | JSON[] | number | string | boolean | null; export type PropertiesSchema = { [K in keyof T]-?: - | (UncheckedJSONSchemaType & Nullable) - | { $ref: string }; + (UncheckedJSONSchemaType & Nullable) | { $ref: string }; }; export type RequiredMembers = { diff --git a/server/utils/kyselyTransactionWithRetry.ts b/server/utils/kyselyTransactionWithRetry.ts index bb4e5f3a..fb226225 100644 --- a/server/utils/kyselyTransactionWithRetry.ts +++ b/server/utils/kyselyTransactionWithRetry.ts @@ -32,8 +32,7 @@ export function makeKyselyTransactionWithRetry(kysely: Kysely) { ): Promise; async function transactionWithRetry( optionsOrCallback: - | TransactionWithRetryOptions - | ((trx: Transaction) => Promise), + TransactionWithRetryOptions | ((trx: Transaction) => Promise), maybeCallback?: (trx: Transaction) => Promise, ): Promise { const [options, callback] = diff --git a/server/utils/typescript-types.ts b/server/utils/typescript-types.ts index adfe91c5..e383a00c 100644 --- a/server/utils/typescript-types.ts +++ b/server/utils/typescript-types.ts @@ -22,8 +22,7 @@ export type { * recursively transforms the keys of objects in array/tuple types. */ export type SnakeCasedPropertiesDeepWithArrays = T extends - | readonly [] - | readonly [...never[]] + readonly [] | readonly [...never[]] ? readonly [] : T extends readonly [infer U, ...infer V] ? readonly [ @@ -39,9 +38,9 @@ export type SnakeCasedPropertiesDeepWithArrays = T extends ? ReadonlyArray> : T extends object ? { - [K in keyof T as SnakeCase]: SnakeCasedPropertiesDeepWithArrays< - T[K] - >; + [ + K in keyof T as SnakeCase + ]: SnakeCasedPropertiesDeepWithArrays; } : T; diff --git a/types/index.ts b/types/index.ts index e98fbb36..f1a0a814 100644 --- a/types/index.ts +++ b/types/index.ts @@ -197,9 +197,7 @@ export function getScalarType(it: Field) { } export type MediaKind = - | ScalarTypes['AUDIO'] - | ScalarTypes['IMAGE'] - | ScalarTypes['VIDEO']; + ScalarTypes['AUDIO'] | ScalarTypes['IMAGE'] | ScalarTypes['VIDEO']; export function isMediaType(it: ScalarType): boolean { return ( From 4c8aa82f1b2392addea906b7ef999200f9cfe69a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tao=20Bojl=C3=A9n?= Date: Tue, 28 Jul 2026 15:38:27 +0100 Subject: [PATCH 43/57] fix(mrt): collapse long text fields with Read more (#870) (#903) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(mrt): add memoized CollapsibleText component (#870) Co-Authored-By: pi * feat(mrt): collapse long STRING fields with Read more (#870) Co-Authored-By: pi * fix(mrt): drop horizontal scroll for text containers (#870) Co-Authored-By: pi * fix(mrt): remove page-wide horizontal scroll from review view (#870) Co-Authored-By: pi * test(mrt): strengthen CollapsibleText grapheme test, fix stale comment (#870) Co-Authored-By: pi * fix(mrt): allow string field flex item to shrink for text wrapping (#870) The FieldComponent wrapper sat inside FieldsComponent's flex flex-wrap container with the default min-width: auto, so a flex item containing a long unbroken string expanded to the string's intrinsic width instead of wrapping. break-words and WebkitLineClamp only take effect when the element has a bounded content width, so the CollapsibleText Read more toggle appeared but did nothing on huge unbroken tokens. Adding min-w-0 lets the flex item shrink below its content width so wrapping and the line clamp take effect. Co-Authored-By: pi * fix(mrt): short-circuit grapheme count, reset expanded on text change (#870) Address review feedback on CollapsibleText: - countGraphemes → exceedsGraphemeThreshold: stop iterating once the count is known to exceed maxGraphemes, so a 1MB string segments at most maxGraphemes+1 graphemes instead of all of them. - Reset the expanded state when the text prop changes. When navigating between review jobs that reuse the same field name, React reuses the CollapsibleText instance; without the reset, a newly loaded long value inherited the previous job's expanded state. - The custom-thresholds test now asserts the maxLines prop lands on the clamped div's WebkitLineClamp style, independently verifying the line-count constraint (previously maxLines was passed but untested). - New test: expanding one long value then rerendering with a different long value restores the collapsed state. Co-Authored-By: pi * test(mrt): strengthen collapsible text thresholds (#870) Co-Authored-By: pi --- .../v2/ManualReviewJobContentView.tsx | 2 +- .../v2/ManualReviewJobFieldsComponent.tsx | 26 ++-- .../v2/components/CollapsibleText.test.tsx | 114 ++++++++++++++++++ .../v2/components/CollapsibleText.tsx | 89 ++++++++++++++ 4 files changed, 223 insertions(+), 8 deletions(-) create mode 100644 client/src/webpages/dashboard/mrt/manual_review_job/v2/components/CollapsibleText.test.tsx create mode 100644 client/src/webpages/dashboard/mrt/manual_review_job/v2/components/CollapsibleText.tsx diff --git a/client/src/webpages/dashboard/mrt/manual_review_job/v2/ManualReviewJobContentView.tsx b/client/src/webpages/dashboard/mrt/manual_review_job/v2/ManualReviewJobContentView.tsx index dc88bd81..1c500b8c 100644 --- a/client/src/webpages/dashboard/mrt/manual_review_job/v2/ManualReviewJobContentView.tsx +++ b/client/src/webpages/dashboard/mrt/manual_review_job/v2/ManualReviewJobContentView.tsx @@ -131,7 +131,7 @@ export default function ManualReviewJobContentView(props: { ); return ( -
+
{/* Split the data into two columns: non-media fields and media fields*/}
diff --git a/client/src/webpages/dashboard/mrt/manual_review_job/v2/ManualReviewJobFieldsComponent.tsx b/client/src/webpages/dashboard/mrt/manual_review_job/v2/ManualReviewJobFieldsComponent.tsx index 347011ac..ef1bd1f1 100644 --- a/client/src/webpages/dashboard/mrt/manual_review_job/v2/ManualReviewJobFieldsComponent.tsx +++ b/client/src/webpages/dashboard/mrt/manual_review_job/v2/ManualReviewJobFieldsComponent.tsx @@ -14,6 +14,7 @@ import ReactAudioPlayer from 'react-audio-player'; import { Link } from 'react-router-dom'; import ComponentLoading from '../../../../../components/common/ComponentLoading'; +import CollapsibleText from '@/webpages/dashboard/mrt/manual_review_job/v2/components/CollapsibleText'; import { GQLContentItem, @@ -190,12 +191,23 @@ function TableRowComponent(props: {
); } + case 'STRING': { + return ( +
+ {label ? ( +
+ {label} +
+ ) : null} + +
+ ); + } case 'BOOLEAN': case 'GEOHASH': case 'ID': case 'NUMBER': case 'POLICY_ID': - case 'STRING': case 'EMAIL_ADDRESS': { // EMAIL_ADDRESS renders as plain text for now; a follow-up could make // it a mailto/pivot link the way IP_ADDRESS pivots on the IP. @@ -528,7 +540,7 @@ function FieldComponent(props: { case 'EMAIL_ADDRESS': case 'DATETIME': return ( -
+
{!hideLabels ? (
@@ -646,7 +658,7 @@ function ContainerComponent(props: { type: data.container!.valueScalarType, }; return ( -
+
{/*Talk to ethan about how to avoid casting here*/} ) : null}
diff --git a/client/src/webpages/dashboard/mrt/manual_review_job/v2/components/CollapsibleText.test.tsx b/client/src/webpages/dashboard/mrt/manual_review_job/v2/components/CollapsibleText.test.tsx new file mode 100644 index 00000000..94a54d24 --- /dev/null +++ b/client/src/webpages/dashboard/mrt/manual_review_job/v2/components/CollapsibleText.test.tsx @@ -0,0 +1,114 @@ +import { fireEvent, render, screen } from '@testing-library/react'; +import React from 'react'; + +import '@testing-library/jest-dom/extend-expect'; + +import CollapsibleText from '@/webpages/dashboard/mrt/manual_review_job/v2/components/CollapsibleText'; + +describe('CollapsibleText', () => { + it('renders short text in full without a Read more button', () => { + render(); + expect(screen.getByText('hello world')).toBeInTheDocument(); + expect(screen.queryByRole('button')).not.toBeInTheDocument(); + }); + + it('collapses text exceeding maxGraphemes and shows Read more', () => { + const longText = 'a'.repeat(2001); + render(); + // The full text is in the DOM (CSS line-clamp hides overflow visually, not in the DOM). + expect(screen.getByText(longText)).toBeInTheDocument(); + expect( + screen.getByRole('button', { name: /read more/i }), + ).toBeInTheDocument(); + }); + + it('expands to full text and toggles to Read less on click', () => { + const longText = 'a'.repeat(2001); + render(); + const moreButton = screen.getByRole('button', { name: /read more/i }); + fireEvent.click(moreButton); + expect( + screen.getByRole('button', { name: /read less/i }), + ).toBeInTheDocument(); + // Full text is now rendered (not clamped). + expect(screen.getByText(longText)).toBeInTheDocument(); + }); + + it('collapses again on Read less click', () => { + const longText = 'a'.repeat(2001); + render(); + fireEvent.click(screen.getByRole('button', { name: /read more/i })); + fireEvent.click(screen.getByRole('button', { name: /read less/i })); + expect( + screen.getByRole('button', { name: /read more/i }), + ).toBeInTheDocument(); + }); + + it('counts graphemes, not UTF-16 code units (emoji with skin tone)', () => { + // 👨🏿 is a single grapheme but 4 UTF-16 code units. A naive `.length` + // check would collapse at 501 such graphemes (length 2004 > 2000), but + // a correct grapheme count (501) stays under the threshold → no collapse. + const grapheme = '👨🏿'; + const underThresholdByGrapheme = grapheme.repeat(501); + expect(underThresholdByGrapheme.length).toBeGreaterThan(2000); + render(); + expect(screen.queryByRole('button')).not.toBeInTheDocument(); + + // 2001 graphemes (8016 UTF-16 code units) does exceed the grapheme + // threshold → collapses. + const overThresholdByGrapheme = grapheme.repeat(2001); + render(); + expect( + screen.getByRole('button', { name: /read more/i }), + ).toBeInTheDocument(); + }); + + it('respects a custom maxGraphemes threshold', () => { + // 11 chars, under default maxGraphemes (2000) but over custom maxGraphemes (10). + render(); + expect( + screen.getByRole('button', { name: /read more/i }), + ).toBeInTheDocument(); + }); + + it('respects a custom maxLines threshold for wrapping text', () => { + const wrappingText = 'wrap '.repeat(20).trim(); + const { container, rerender } = render( +
+ +
, + ); + const clampedDiv = container.querySelector( + 'div[style*="-webkit-box"]', + ) as HTMLElement; + expect(clampedDiv.style.webkitLineClamp).toBe('2'); + + rerender( +
+ +
, + ); + expect(clampedDiv.style.webkitLineClamp).toBe('3'); + }); + + it('does not collapse text at exactly maxGraphemes', () => { + render(); + expect(screen.queryByRole('button')).not.toBeInTheDocument(); + }); + + it('resets to collapsed when the text prop changes', () => { + // Simulates navigating between review jobs that reuse the same field name + // (React reuses the CollapsibleText instance). Expanding one long value, + //then rendering a different long value, must restore the collapsed state. + const { rerender } = render(); + fireEvent.click(screen.getByRole('button', { name: /read more/i })); + expect( + screen.getByRole('button', { name: /read less/i }), + ).toBeInTheDocument(); + + rerender(); + expect( + screen.getByRole('button', { name: /read more/i }), + ).toBeInTheDocument(); + }); +}); diff --git a/client/src/webpages/dashboard/mrt/manual_review_job/v2/components/CollapsibleText.tsx b/client/src/webpages/dashboard/mrt/manual_review_job/v2/components/CollapsibleText.tsx new file mode 100644 index 00000000..40465287 --- /dev/null +++ b/client/src/webpages/dashboard/mrt/manual_review_job/v2/components/CollapsibleText.tsx @@ -0,0 +1,89 @@ +import { memo, useEffect, useMemo, useState } from 'react'; + +type CollapsibleTextProps = { + text: string; + maxLines?: number; + maxGraphemes?: number; +}; + +/** + * Count graphemes using Intl.Segmenter (handles all scripts correctly), + * stopping early once the count is known to exceed `threshold`. This avoids + * segmenting an entire pathological paste (e.g. a 1MB string) when we only + * need to know whether it crosses the collapse threshold. + */ +function exceedsGraphemeThreshold(text: string, threshold: number): boolean { + const segmenter = new Intl.Segmenter(undefined, { granularity: 'grapheme' }); + let count = 0; + for (const _ of segmenter.segment(text)) { + count += 1; + if (count > threshold) { + return true; + } + } + return false; +} + +/** + * Renders text with wrapping. When the text exceeds `maxGraphemes`, it is + * collapsed to `maxLines` visible lines with a "Read more" / "Read less" + * toggle. Short text renders in full with no toggle. + * + * Memoized: props are primitives (string, number), so React.memo's shallow + * comparison prevents re-renders (and re-segmentation) when a parent re-renders + * but the text hasn't changed — important for thread histories with many copies + * of the same message. + */ +function CollapsibleTextImpl({ + text, + maxLines = 12, + maxGraphemes = 2000, +}: CollapsibleTextProps) { + const isCollapsible = useMemo( + () => exceedsGraphemeThreshold(text, maxGraphemes), + [text, maxGraphemes], + ); + const [expanded, setExpanded] = useState(false); + + // When the text prop changes (e.g. navigating between review jobs that reuse + // the same field name → React reuses this component instance), reset to the + // collapsed state so newly loaded long content doesn't inherit the previous + // job's expanded view. + useEffect(() => { + setExpanded(false); + }, [text]); + + if (!isCollapsible) { + return ( +
{text}
+ ); + } + + return ( +
+
+ {text} +
+ +
+ ); +} + +const CollapsibleText = memo(CollapsibleTextImpl); +export default CollapsibleText; From fd76bc22f6555a273d37366007acf7e5adf7d8c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tao=20Bojl=C3=A9n?= Date: Tue, 28 Jul 2026 16:10:01 +0100 Subject: [PATCH 44/57] Update npm deps with security vulnerabilities (#902) Reconcile the client and server lockfiles against current main, then apply the non-breaking fixes available through npm audit fix. Client updates include the Babel 7.29.7 security release and patched brace-expansion versions. Server updates include js-yaml 3.15.0, protobufjs 7.6.5, patched brace-expansion versions, and compatible Cassandra dependency resolution. Remaining audit findings require breaking upgrades across React Router, ESLint/Storybook, Cassandra, or Jest. Keep those separately scoped; the existing dev-only piscina risk remains accepted here. Co-Authored-By: pi-coding-agent Co-Authored-By: Codex --- client/package-lock.json | 193 ++++++++++++++++++++------------------- server/package-lock.json | 64 ++++++++----- 2 files changed, 138 insertions(+), 119 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index 83caca8d..05b4af2a 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -270,13 +270,13 @@ "license": "MIT" }, "node_modules/@babel/code-frame": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", - "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.28.5", + "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" }, @@ -285,9 +285,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", - "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", "dev": true, "license": "MIT", "engines": { @@ -295,21 +295,21 @@ } }, "node_modules/@babel/core": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", - "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-module-transforms": "^7.28.6", - "@babel/helpers": "^7.28.6", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/traverse": "^7.29.0", - "@babel/types": "^7.29.0", + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", @@ -341,14 +341,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.29.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", - "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz", + "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.29.0", - "@babel/types": "^7.29.0", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -358,14 +358,14 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", - "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.28.6", - "@babel/helper-validator-option": "^7.27.1", + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" @@ -379,14 +379,15 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" } }, "node_modules/@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", "dev": true, "license": "MIT", "engines": { @@ -394,29 +395,29 @@ } }, "node_modules/@babel/helper-module-imports": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", - "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^7.28.6", - "@babel/types": "^7.28.6" + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", - "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.28.6", - "@babel/helper-validator-identifier": "^7.28.5", - "@babel/traverse": "^7.28.6" + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -436,9 +437,9 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", "dev": true, "license": "MIT", "engines": { @@ -446,9 +447,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", "dev": true, "license": "MIT", "engines": { @@ -456,9 +457,9 @@ } }, "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", "dev": true, "license": "MIT", "engines": { @@ -466,27 +467,27 @@ } }, "node_modules/@babel/helpers": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", - "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.28.6", - "@babel/types": "^7.28.6" + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz", - "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", + "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.29.0" + "@babel/types": "^7.29.7" }, "bin": { "parser": "bin/babel-parser.js" @@ -550,33 +551,33 @@ } }, "node_modules/@babel/template": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", - "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.28.6", - "@babel/parser": "^7.28.6", - "@babel/types": "^7.28.6" + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", - "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz", + "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/types": "^7.29.0", + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7", "debug": "^4.3.1" }, "engines": { @@ -584,14 +585,14 @@ } }, "node_modules/@babel/types": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", - "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", + "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1708,9 +1709,9 @@ } }, "node_modules/@joshwooding/vite-plugin-react-docgen-typescript/node_modules/brace-expansion": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz", - "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.3.tgz", + "integrity": "sha512-DRdx5neNsG/QXbniLFWi2YmC/68oeOOmKz6zOjVk6ZS1ZLXgLIKqVEc6hWsmkjBbgii0SwaBTcJ5XKj5gzY/4A==", "dev": true, "license": "MIT", "dependencies": { @@ -6645,9 +6646,9 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", - "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", + "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -8235,9 +8236,9 @@ } }, "node_modules/filelist/node_modules/brace-expansion": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz", - "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.3.tgz", + "integrity": "sha512-DRdx5neNsG/QXbniLFWi2YmC/68oeOOmKz6zOjVk6ZS1ZLXgLIKqVEc6hWsmkjBbgii0SwaBTcJ5XKj5gzY/4A==", "dev": true, "license": "MIT", "dependencies": { @@ -10052,6 +10053,7 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, + "license": "ISC", "dependencies": { "yallist": "^3.0.2" } @@ -13704,16 +13706,16 @@ } }, "node_modules/temp/node_modules/brace-expansion": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", - "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz", + "integrity": "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, "engines": { - "node": "18 || 20 || >=22" + "node": "20 || >=22" } }, "node_modules/temp/node_modules/glob": { @@ -15318,7 +15320,8 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/yaml": { "version": "2.9.0", diff --git a/server/package-lock.json b/server/package-lock.json index 22a61c2f..0d47eef7 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -2854,9 +2854,9 @@ } }, "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", - "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.0.tgz", + "integrity": "sha512-ttBQIIQPDeLjpPOohtUdXuXUVoA2uIB6fEH9HyJ7234s5mBJ5wTx20njxplLZQgLaOfpmPQA7X2t5AX6tIPbog==", "dev": true, "license": "MIT", "dependencies": { @@ -12539,9 +12539,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz", - "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.3.tgz", + "integrity": "sha512-DRdx5neNsG/QXbniLFWi2YmC/68oeOOmKz6zOjVk6ZS1ZLXgLIKqVEc6hWsmkjBbgii0SwaBTcJ5XKj5gzY/4A==", "dev": true, "license": "MIT", "dependencies": { @@ -13192,11 +13192,12 @@ } }, "node_modules/adm-zip": { - "version": "0.5.10", - "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.10.tgz", - "integrity": "sha512-x0HvcHqVJNTPk/Bw8JbLWlWoo6Wwnsug0fnYYro1HBrjxZ3G7/AZk7Ahv8JwDe1uIcz8eBqvu86FuF1POiG7vQ==", + "version": "0.5.18", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.18.tgz", + "integrity": "sha512-ufJnssQGbxzLNS1Ho9bCtX4rQKCCvoVuDLHoJyc3F9dOGDB4BkWs2Ci0kv53lqocAEQ/Cbi+I2XCsNYGqVYqng==", + "license": "MIT", "engines": { - "node": ">=6.0" + "node": ">=12.0" } }, "node_modules/agent-base": { @@ -13760,9 +13761,9 @@ "license": "MIT" }, "node_modules/brace-expansion": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", - "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", + "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", "dev": true, "license": "MIT", "dependencies": { @@ -13948,17 +13949,26 @@ "license": "CC-BY-4.0" }, "node_modules/cassandra-driver": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/cassandra-driver/-/cassandra-driver-4.8.0.tgz", - "integrity": "sha512-HritfMGq9V7SuESeSodHvArs0mLuMk7uh+7hQK2lqdvXrvm50aWxb4RPxkK3mPDdsgHjJ427xNRFITMH2ei+Sw==", + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/cassandra-driver/-/cassandra-driver-4.9.0.tgz", + "integrity": "sha512-svYpdkLIGjD0WmuuwkkeYbfBdPX1zksK2cDyT1mWjX53OVTzuWBOVy54K6PPij8GgYpIG+K82OryrBv/xNeuWg==", "license": "Apache-2.0", "dependencies": { - "@types/node": "^18.11.18", + "@types/node": "^20.14.8", "adm-zip": "~0.5.10", "long": "~5.2.3" }, "engines": { - "node": ">=18" + "node": ">=20" + } + }, + "node_modules/cassandra-driver/node_modules/@types/node": { + "version": "20.19.43", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz", + "integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" } }, "node_modules/cassandra-driver/node_modules/long": { @@ -13967,6 +13977,12 @@ "integrity": "sha512-e0r9YBBgNCq1D1o5Dp8FMH0N5hsFtXDBiVa0qoJPHpakvZkmDKPRoGffZJII/XsHvj9An9blm+cRJ01yQqU+Dw==", "license": "Apache-2.0" }, + "node_modules/cassandra-driver/node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "license": "MIT" + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -20023,9 +20039,9 @@ } }, "node_modules/protobufjs": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.4.tgz", - "integrity": "sha512-RJJPTTpvFfHcWLkIa2JFWK4XvtSzS0yEWDmunqHXli1h3JlkbcQZXDZdcWxv+JK3Xsl5/UFDPZ0iGm7DAengYw==", + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.5.tgz", + "integrity": "sha512-/FPD0nUc9jH6rfFjji9IBqOz4pcSE3CsT1m7Ep6Mdb0LxSUMj8hgl6GomOvZzpNpAqqGaXA0P3VSrZLFzIhQrw==", "hasInstallScript": true, "license": "BSD-3-Clause", "dependencies": { @@ -20462,9 +20478,9 @@ } }, "node_modules/rimraf/node_modules/brace-expansion": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz", - "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.3.tgz", + "integrity": "sha512-DRdx5neNsG/QXbniLFWi2YmC/68oeOOmKz6zOjVk6ZS1ZLXgLIKqVEc6hWsmkjBbgii0SwaBTcJ5XKj5gzY/4A==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" From 57f1c5cb81227ca870c0dd12f6419be152f63c07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tao=20Bojl=C3=A9n?= Date: Tue, 28 Jul 2026 16:32:52 +0100 Subject: [PATCH 45/57] Eliminate manual review history item refetches (#951) * Eliminate manual review history item refetches Co-Authored-By: Codex * Remove manual review history regression test Co-Authored-By: Codex --------- Co-authored-by: Codex --- .../v2/ContentRelatedItemComponent.tsx | 23 ++++++++----------- ...obLatestSubmissionsWithThreadComponent.tsx | 9 +++++--- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/client/src/webpages/dashboard/mrt/manual_review_job/v2/ContentRelatedItemComponent.tsx b/client/src/webpages/dashboard/mrt/manual_review_job/v2/ContentRelatedItemComponent.tsx index a767cd08..340c03db 100644 --- a/client/src/webpages/dashboard/mrt/manual_review_job/v2/ContentRelatedItemComponent.tsx +++ b/client/src/webpages/dashboard/mrt/manual_review_job/v2/ContentRelatedItemComponent.tsx @@ -1,10 +1,11 @@ -import { useGQLGetRelatedItemsQuery } from '@/graphql/generated'; +import { GQLContentItem } from '@/graphql/generated'; import { ItemTypeFieldFieldData } from '@/webpages/dashboard/item_types/itemTypeUtils'; import { gql } from '@apollo/client'; -import { ItemIdentifier } from '@roostorg/coop-types'; import FieldsComponent from './ManualReviewJobFieldsComponent'; +// ManualReviewJobFieldsComponent uses this operation to resolve RELATED_ITEM +// fields. gql` query getRelatedItems($itemIdentifiers: [ItemIdentifierInput!]!) { latestItemSubmissions(itemIdentifiers: $itemIdentifiers) { @@ -83,21 +84,17 @@ gql` } } `; + +type LoadedContentItem = Pick & { + type: Pick; +}; + export default function ContentRelatedItemComponent(props: { - relatedItem: ItemIdentifier; + item: LoadedContentItem; unblurAllMedia: boolean; title: string; }) { - const { relatedItem, unblurAllMedia } = props; - const { data, error } = useGQLGetRelatedItemsQuery({ - variables: { - itemIdentifiers: [relatedItem], - }, - }); - if (!data || error) { - return null; - } - const item = data.latestItemSubmissions[0]; + const { item, unblurAllMedia } = props; const fieldData = item.type.baseFields.map( ( diff --git a/client/src/webpages/dashboard/mrt/manual_review_job/v2/user/ManualReviewJobLatestSubmissionsWithThreadComponent.tsx b/client/src/webpages/dashboard/mrt/manual_review_job/v2/user/ManualReviewJobLatestSubmissionsWithThreadComponent.tsx index e16f9390..e2231361 100644 --- a/client/src/webpages/dashboard/mrt/manual_review_job/v2/user/ManualReviewJobLatestSubmissionsWithThreadComponent.tsx +++ b/client/src/webpages/dashboard/mrt/manual_review_job/v2/user/ManualReviewJobLatestSubmissionsWithThreadComponent.tsx @@ -255,9 +255,12 @@ export default function ManualReviewJobLatestSubmissionsWithThreadComponent(prop } return ( Date: Tue, 28 Jul 2026 16:46:56 +0100 Subject: [PATCH 46/57] Move related item query to its consumer (#952) Co-authored-by: Codex --- client/src/graphql/generated.ts | 556 +++++++++--------- .../v2/ContentRelatedItemComponent.tsx | 82 --- .../v2/ManualReviewJobFieldsComponent.tsx | 79 +++ 3 files changed, 357 insertions(+), 360 deletions(-) diff --git a/client/src/graphql/generated.ts b/client/src/graphql/generated.ts index 3117604f..6c530df6 100644 --- a/client/src/graphql/generated.ts +++ b/client/src/graphql/generated.ts @@ -16674,104 +16674,6 @@ export type GQLJobFieldsFragment = { }; }; -export type GQLGetRelatedItemsQueryVariables = Exact<{ - itemIdentifiers: - ReadonlyArray | GQLItemIdentifierInput; -}>; - -export type GQLGetRelatedItemsQuery = { - readonly __typename: 'Query'; - readonly latestItemSubmissions: ReadonlyArray< - | { - readonly __typename: 'ContentItem'; - readonly id: string; - readonly submissionId: string; - readonly submissionTime?: Date | string | null; - readonly data: JsonObject; - readonly type: { - readonly __typename: 'ContentItemType'; - readonly id: string; - readonly name: string; - readonly baseFields: ReadonlyArray<{ - readonly __typename: 'BaseField'; - readonly name: string; - readonly type: GQLFieldType; - readonly required: boolean; - readonly container?: { - readonly __typename: 'Container'; - readonly containerType: GQLContainerType; - readonly keyScalarType?: GQLScalarType | null; - readonly valueScalarType: GQLScalarType; - } | null; - }>; - readonly schemaFieldRoles: { - readonly __typename: 'ContentSchemaFieldRoles'; - readonly displayName?: string | null; - }; - }; - } - | { - readonly __typename: 'ThreadItem'; - readonly id: string; - readonly submissionId: string; - readonly submissionTime?: Date | string | null; - readonly data: JsonObject; - readonly type: { - readonly __typename: 'ThreadItemType'; - readonly id: string; - readonly name: string; - readonly baseFields: ReadonlyArray<{ - readonly __typename: 'BaseField'; - readonly name: string; - readonly type: GQLFieldType; - readonly required: boolean; - readonly container?: { - readonly __typename: 'Container'; - readonly containerType: GQLContainerType; - readonly keyScalarType?: GQLScalarType | null; - readonly valueScalarType: GQLScalarType; - } | null; - }>; - readonly schemaFieldRoles: { - readonly __typename: 'ThreadSchemaFieldRoles'; - readonly displayName?: string | null; - }; - }; - } - | { - readonly __typename: 'UserItem'; - readonly id: string; - readonly submissionId: string; - readonly submissionTime?: Date | string | null; - readonly data: JsonObject; - readonly type: { - readonly __typename: 'UserItemType'; - readonly id: string; - readonly name: string; - readonly baseFields: ReadonlyArray<{ - readonly __typename: 'BaseField'; - readonly name: string; - readonly type: GQLFieldType; - readonly required: boolean; - readonly container?: { - readonly __typename: 'Container'; - readonly containerType: GQLContainerType; - readonly keyScalarType?: GQLScalarType | null; - readonly valueScalarType: GQLScalarType; - } | null; - }>; - readonly schemaFieldRoles: { - readonly __typename: 'UserSchemaFieldRoles'; - readonly displayName?: string | null; - readonly createdAt?: string | null; - readonly profileIcon?: string | null; - readonly backgroundImage?: string | null; - }; - }; - } - >; -}; - export type GQLManualReviewJobCommentFieldsFragment = { readonly __typename: 'ManualReviewJobComment'; readonly id: string; @@ -16885,6 +16787,104 @@ export type GQLGetThreadHistoryQuery = { }>; }; +export type GQLGetRelatedItemsQueryVariables = Exact<{ + itemIdentifiers: + ReadonlyArray | GQLItemIdentifierInput; +}>; + +export type GQLGetRelatedItemsQuery = { + readonly __typename: 'Query'; + readonly latestItemSubmissions: ReadonlyArray< + | { + readonly __typename: 'ContentItem'; + readonly id: string; + readonly submissionId: string; + readonly submissionTime?: Date | string | null; + readonly data: JsonObject; + readonly type: { + readonly __typename: 'ContentItemType'; + readonly id: string; + readonly name: string; + readonly baseFields: ReadonlyArray<{ + readonly __typename: 'BaseField'; + readonly name: string; + readonly type: GQLFieldType; + readonly required: boolean; + readonly container?: { + readonly __typename: 'Container'; + readonly containerType: GQLContainerType; + readonly keyScalarType?: GQLScalarType | null; + readonly valueScalarType: GQLScalarType; + } | null; + }>; + readonly schemaFieldRoles: { + readonly __typename: 'ContentSchemaFieldRoles'; + readonly displayName?: string | null; + }; + }; + } + | { + readonly __typename: 'ThreadItem'; + readonly id: string; + readonly submissionId: string; + readonly submissionTime?: Date | string | null; + readonly data: JsonObject; + readonly type: { + readonly __typename: 'ThreadItemType'; + readonly id: string; + readonly name: string; + readonly baseFields: ReadonlyArray<{ + readonly __typename: 'BaseField'; + readonly name: string; + readonly type: GQLFieldType; + readonly required: boolean; + readonly container?: { + readonly __typename: 'Container'; + readonly containerType: GQLContainerType; + readonly keyScalarType?: GQLScalarType | null; + readonly valueScalarType: GQLScalarType; + } | null; + }>; + readonly schemaFieldRoles: { + readonly __typename: 'ThreadSchemaFieldRoles'; + readonly displayName?: string | null; + }; + }; + } + | { + readonly __typename: 'UserItem'; + readonly id: string; + readonly submissionId: string; + readonly submissionTime?: Date | string | null; + readonly data: JsonObject; + readonly type: { + readonly __typename: 'UserItemType'; + readonly id: string; + readonly name: string; + readonly baseFields: ReadonlyArray<{ + readonly __typename: 'BaseField'; + readonly name: string; + readonly type: GQLFieldType; + readonly required: boolean; + readonly container?: { + readonly __typename: 'Container'; + readonly containerType: GQLContainerType; + readonly keyScalarType?: GQLScalarType | null; + readonly valueScalarType: GQLScalarType; + } | null; + }>; + readonly schemaFieldRoles: { + readonly __typename: 'UserSchemaFieldRoles'; + readonly displayName?: string | null; + readonly createdAt?: string | null; + readonly profileIcon?: string | null; + readonly backgroundImage?: string | null; + }; + }; + } + >; +}; + export type GQLItemTypeHiddenFieldsQueryVariables = Exact<{ [key: string]: never; }>; @@ -34964,185 +34964,11 @@ export type GQLReleaseJobLockMutationOptions = Apollo.BaseMutationOptions< GQLReleaseJobLockMutation, GQLReleaseJobLockMutationVariables >; -export const GQLGetRelatedItemsDocument = gql` - query getRelatedItems($itemIdentifiers: [ItemIdentifierInput!]!) { - latestItemSubmissions(itemIdentifiers: $itemIdentifiers) { - ... on UserItem { - id - submissionId - submissionTime - data - type { - id - name - baseFields { - name - type - required - container { - containerType - keyScalarType - valueScalarType - } - } - schemaFieldRoles { - displayName - createdAt - profileIcon - backgroundImage - } - } - } - ... on ContentItem { - id - submissionId - submissionTime - data - type { - id - name - baseFields { - name - type - required - container { - containerType - keyScalarType - valueScalarType - } - } - schemaFieldRoles { - displayName - } - } - } - ... on ThreadItem { - id - submissionId - submissionTime - data - type { - id - name - baseFields { - name - type - required - container { - containerType - keyScalarType - valueScalarType - } - } - schemaFieldRoles { - displayName - } - } - } - } - } -`; - -/** - * __useGQLGetRelatedItemsQuery__ - * - * To run a query within a React component, call `useGQLGetRelatedItemsQuery` and pass it any options that fit your needs. - * When your component renders, `useGQLGetRelatedItemsQuery` returns an object from Apollo Client that contains loading, error, and data properties - * you can use to render your UI. - * - * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; - * - * @example - * const { data, loading, error } = useGQLGetRelatedItemsQuery({ - * variables: { - * itemIdentifiers: // value for 'itemIdentifiers' - * }, - * }); - */ -export function useGQLGetRelatedItemsQuery( - baseOptions: Apollo.QueryHookOptions< - GQLGetRelatedItemsQuery, - GQLGetRelatedItemsQueryVariables - > & - ( - | { variables: GQLGetRelatedItemsQueryVariables; skip?: boolean } - | { skip: boolean } - ), -) { - const options = { ...defaultOptions, ...baseOptions }; - return Apollo.useQuery< - GQLGetRelatedItemsQuery, - GQLGetRelatedItemsQueryVariables - >(GQLGetRelatedItemsDocument, options); -} -export function useGQLGetRelatedItemsLazyQuery( - baseOptions?: Apollo.LazyQueryHookOptions< - GQLGetRelatedItemsQuery, - GQLGetRelatedItemsQueryVariables - >, -) { - const options = { ...defaultOptions, ...baseOptions }; - return Apollo.useLazyQuery< - GQLGetRelatedItemsQuery, - GQLGetRelatedItemsQueryVariables - >(GQLGetRelatedItemsDocument, options); -} -// @ts-ignore -export function useGQLGetRelatedItemsSuspenseQuery( - baseOptions?: Apollo.SuspenseQueryHookOptions< - GQLGetRelatedItemsQuery, - GQLGetRelatedItemsQueryVariables - >, -): Apollo.UseSuspenseQueryResult< - GQLGetRelatedItemsQuery, - GQLGetRelatedItemsQueryVariables ->; -export function useGQLGetRelatedItemsSuspenseQuery( - baseOptions?: - | Apollo.SkipToken - | Apollo.SuspenseQueryHookOptions< - GQLGetRelatedItemsQuery, - GQLGetRelatedItemsQueryVariables - >, -): Apollo.UseSuspenseQueryResult< - GQLGetRelatedItemsQuery | undefined, - GQLGetRelatedItemsQueryVariables ->; -export function useGQLGetRelatedItemsSuspenseQuery( - baseOptions?: - | Apollo.SkipToken - | Apollo.SuspenseQueryHookOptions< - GQLGetRelatedItemsQuery, - GQLGetRelatedItemsQueryVariables - >, -) { - const options = - baseOptions === Apollo.skipToken - ? baseOptions - : { ...defaultOptions, ...baseOptions }; - return Apollo.useSuspenseQuery< - GQLGetRelatedItemsQuery, - GQLGetRelatedItemsQueryVariables - >(GQLGetRelatedItemsDocument, options); -} -export type GQLGetRelatedItemsQueryHookResult = ReturnType< - typeof useGQLGetRelatedItemsQuery ->; -export type GQLGetRelatedItemsLazyQueryHookResult = ReturnType< - typeof useGQLGetRelatedItemsLazyQuery ->; -export type GQLGetRelatedItemsSuspenseQueryHookResult = ReturnType< - typeof useGQLGetRelatedItemsSuspenseQuery ->; -export type GQLGetRelatedItemsQueryResult = Apollo.QueryResult< - GQLGetRelatedItemsQuery, - GQLGetRelatedItemsQueryVariables ->; -export const GQLGetCommentsForJobDocument = gql` - query GetCommentsForJob($jobId: ID!) { - getCommentsForJob(jobId: $jobId) { - ... on ManualReviewJobComment { - ...ManualReviewJobCommentFields +export const GQLGetCommentsForJobDocument = gql` + query GetCommentsForJob($jobId: ID!) { + getCommentsForJob(jobId: $jobId) { + ... on ManualReviewJobComment { + ...ManualReviewJobCommentFields } } } @@ -35486,6 +35312,180 @@ export type GQLGetThreadHistoryQueryResult = Apollo.QueryResult< GQLGetThreadHistoryQuery, GQLGetThreadHistoryQueryVariables >; +export const GQLGetRelatedItemsDocument = gql` + query getRelatedItems($itemIdentifiers: [ItemIdentifierInput!]!) { + latestItemSubmissions(itemIdentifiers: $itemIdentifiers) { + ... on UserItem { + id + submissionId + submissionTime + data + type { + id + name + baseFields { + name + type + required + container { + containerType + keyScalarType + valueScalarType + } + } + schemaFieldRoles { + displayName + createdAt + profileIcon + backgroundImage + } + } + } + ... on ContentItem { + id + submissionId + submissionTime + data + type { + id + name + baseFields { + name + type + required + container { + containerType + keyScalarType + valueScalarType + } + } + schemaFieldRoles { + displayName + } + } + } + ... on ThreadItem { + id + submissionId + submissionTime + data + type { + id + name + baseFields { + name + type + required + container { + containerType + keyScalarType + valueScalarType + } + } + schemaFieldRoles { + displayName + } + } + } + } + } +`; + +/** + * __useGQLGetRelatedItemsQuery__ + * + * To run a query within a React component, call `useGQLGetRelatedItemsQuery` and pass it any options that fit your needs. + * When your component renders, `useGQLGetRelatedItemsQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useGQLGetRelatedItemsQuery({ + * variables: { + * itemIdentifiers: // value for 'itemIdentifiers' + * }, + * }); + */ +export function useGQLGetRelatedItemsQuery( + baseOptions: Apollo.QueryHookOptions< + GQLGetRelatedItemsQuery, + GQLGetRelatedItemsQueryVariables + > & + ( + | { variables: GQLGetRelatedItemsQueryVariables; skip?: boolean } + | { skip: boolean } + ), +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useQuery< + GQLGetRelatedItemsQuery, + GQLGetRelatedItemsQueryVariables + >(GQLGetRelatedItemsDocument, options); +} +export function useGQLGetRelatedItemsLazyQuery( + baseOptions?: Apollo.LazyQueryHookOptions< + GQLGetRelatedItemsQuery, + GQLGetRelatedItemsQueryVariables + >, +) { + const options = { ...defaultOptions, ...baseOptions }; + return Apollo.useLazyQuery< + GQLGetRelatedItemsQuery, + GQLGetRelatedItemsQueryVariables + >(GQLGetRelatedItemsDocument, options); +} +// @ts-ignore +export function useGQLGetRelatedItemsSuspenseQuery( + baseOptions?: Apollo.SuspenseQueryHookOptions< + GQLGetRelatedItemsQuery, + GQLGetRelatedItemsQueryVariables + >, +): Apollo.UseSuspenseQueryResult< + GQLGetRelatedItemsQuery, + GQLGetRelatedItemsQueryVariables +>; +export function useGQLGetRelatedItemsSuspenseQuery( + baseOptions?: + | Apollo.SkipToken + | Apollo.SuspenseQueryHookOptions< + GQLGetRelatedItemsQuery, + GQLGetRelatedItemsQueryVariables + >, +): Apollo.UseSuspenseQueryResult< + GQLGetRelatedItemsQuery | undefined, + GQLGetRelatedItemsQueryVariables +>; +export function useGQLGetRelatedItemsSuspenseQuery( + baseOptions?: + | Apollo.SkipToken + | Apollo.SuspenseQueryHookOptions< + GQLGetRelatedItemsQuery, + GQLGetRelatedItemsQueryVariables + >, +) { + const options = + baseOptions === Apollo.skipToken + ? baseOptions + : { ...defaultOptions, ...baseOptions }; + return Apollo.useSuspenseQuery< + GQLGetRelatedItemsQuery, + GQLGetRelatedItemsQueryVariables + >(GQLGetRelatedItemsDocument, options); +} +export type GQLGetRelatedItemsQueryHookResult = ReturnType< + typeof useGQLGetRelatedItemsQuery +>; +export type GQLGetRelatedItemsLazyQueryHookResult = ReturnType< + typeof useGQLGetRelatedItemsLazyQuery +>; +export type GQLGetRelatedItemsSuspenseQueryHookResult = ReturnType< + typeof useGQLGetRelatedItemsSuspenseQuery +>; +export type GQLGetRelatedItemsQueryResult = Apollo.QueryResult< + GQLGetRelatedItemsQuery, + GQLGetRelatedItemsQueryVariables +>; export const GQLItemTypeHiddenFieldsDocument = gql` query ItemTypeHiddenFields { myOrg { @@ -45383,9 +45383,9 @@ export const namedOperations = { GetDecidedJob: 'GetDecidedJob', ManualReviewSafetySettings: 'ManualReviewSafetySettings', ManualReviewJobInfo: 'ManualReviewJobInfo', - getRelatedItems: 'getRelatedItems', GetCommentsForJob: 'GetCommentsForJob', getThreadHistory: 'getThreadHistory', + getRelatedItems: 'getRelatedItems', ItemTypeHiddenFields: 'ItemTypeHiddenFields', AllManualReviewQueues: 'AllManualReviewQueues', getLatestUserSubmittedItemsWithThreads: diff --git a/client/src/webpages/dashboard/mrt/manual_review_job/v2/ContentRelatedItemComponent.tsx b/client/src/webpages/dashboard/mrt/manual_review_job/v2/ContentRelatedItemComponent.tsx index 340c03db..9dd237ae 100644 --- a/client/src/webpages/dashboard/mrt/manual_review_job/v2/ContentRelatedItemComponent.tsx +++ b/client/src/webpages/dashboard/mrt/manual_review_job/v2/ContentRelatedItemComponent.tsx @@ -1,90 +1,8 @@ import { GQLContentItem } from '@/graphql/generated'; import { ItemTypeFieldFieldData } from '@/webpages/dashboard/item_types/itemTypeUtils'; -import { gql } from '@apollo/client'; import FieldsComponent from './ManualReviewJobFieldsComponent'; -// ManualReviewJobFieldsComponent uses this operation to resolve RELATED_ITEM -// fields. -gql` - query getRelatedItems($itemIdentifiers: [ItemIdentifierInput!]!) { - latestItemSubmissions(itemIdentifiers: $itemIdentifiers) { - ... on UserItem { - id - submissionId - submissionTime - data - type { - id - name - baseFields { - name - type - required - container { - containerType - keyScalarType - valueScalarType - } - } - schemaFieldRoles { - displayName - createdAt - profileIcon - backgroundImage - } - } - } - ... on ContentItem { - id - submissionId - submissionTime - data - type { - id - name - baseFields { - name - type - required - container { - containerType - keyScalarType - valueScalarType - } - } - schemaFieldRoles { - displayName - } - } - } - ... on ThreadItem { - id - submissionId - submissionTime - data - type { - id - name - baseFields { - name - type - required - container { - containerType - keyScalarType - valueScalarType - } - } - schemaFieldRoles { - displayName - } - } - } - } - } -`; - type LoadedContentItem = Pick & { type: Pick; }; diff --git a/client/src/webpages/dashboard/mrt/manual_review_job/v2/ManualReviewJobFieldsComponent.tsx b/client/src/webpages/dashboard/mrt/manual_review_job/v2/ManualReviewJobFieldsComponent.tsx index ef1bd1f1..8ac9c4db 100644 --- a/client/src/webpages/dashboard/mrt/manual_review_job/v2/ManualReviewJobFieldsComponent.tsx +++ b/client/src/webpages/dashboard/mrt/manual_review_job/v2/ManualReviewJobFieldsComponent.tsx @@ -91,6 +91,85 @@ type FieldsComponentOptions = { transparentBackground?: boolean; }; +gql` + query getRelatedItems($itemIdentifiers: [ItemIdentifierInput!]!) { + latestItemSubmissions(itemIdentifiers: $itemIdentifiers) { + ... on UserItem { + id + submissionId + submissionTime + data + type { + id + name + baseFields { + name + type + required + container { + containerType + keyScalarType + valueScalarType + } + } + schemaFieldRoles { + displayName + createdAt + profileIcon + backgroundImage + } + } + } + ... on ContentItem { + id + submissionId + submissionTime + data + type { + id + name + baseFields { + name + type + required + container { + containerType + keyScalarType + valueScalarType + } + } + schemaFieldRoles { + displayName + } + } + } + ... on ThreadItem { + id + submissionId + submissionTime + data + type { + id + name + baseFields { + name + type + required + container { + containerType + keyScalarType + valueScalarType + } + } + schemaFieldRoles { + displayName + } + } + } + } + } +`; + gql` query ItemTypeHiddenFields { myOrg { From 386d2d2232fa0ae84cb7bc2e51ff87b00d3a993d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tao=20Bojl=C3=A9n?= Date: Tue, 4 Aug 2026 00:39:11 +0100 Subject: [PATCH 47/57] Fix RetryFailedNcmecDecisionsJob to honor NCMEC_ENV (#928) * test: add regression test for RetryFailedNcmecDecisionsJob NCMEC_ENV * fix: RetryFailedNcmecDecisionsJob honors NCMEC_ENV The retry worker hardcoded isTest=false, so retries in dev/staging (NCMEC_ENV unset or non-production) submitted to the real NCMEC endpoint (report.cybertip.org) instead of the test endpoint (exttest.cybertip.org). It also published post-submit actions unconditionally, unlike the sibling call sites which gate on !isTest. Compute isTest from NCMEC_ENV the same way as the other two submit sites (iocContainer onRecordDecision path and retryNcmecSubmission), pass it to submitReport, and gate publishActions on !isTest. Co-Authored-By: pi * refactor: drop redundant NCMEC_ENV comment in RetryFailedNcmecDecisionsJob Co-Authored-By: pi --- .../RetryFailedNcmecDecisionsJob.test.ts | 277 ++++++++++++++++++ .../RetryFailedNcmecDecisionsJob.ts | 6 +- 2 files changed, 281 insertions(+), 2 deletions(-) create mode 100644 server/workers_jobs/RetryFailedNcmecDecisionsJob.test.ts diff --git a/server/workers_jobs/RetryFailedNcmecDecisionsJob.test.ts b/server/workers_jobs/RetryFailedNcmecDecisionsJob.test.ts new file mode 100644 index 00000000..48a3de45 --- /dev/null +++ b/server/workers_jobs/RetryFailedNcmecDecisionsJob.test.ts @@ -0,0 +1,277 @@ +import { v1 as uuidv1 } from 'uuid'; + +import { + makeSubmissionId, + type ItemSubmissionWithTypeIdentifier, + type NormalizedItemData, +} from '../services/itemProcessingService/index.js'; +import { type ManualReviewToolService } from '../services/manualReviewToolService/index.js'; +import { instantiateOpaqueType } from '../utils/typescript-types.js'; +import makeRetryFailedNcmecDecisionsJob from './RetryFailedNcmecDecisionsJob.js'; + +/** + * Minimal shape of a row returned by + * `ManualReviewToolService.getNcmecDecisions`. Only the fields read by + * `processDecisionRetry` are populated; everything else is omitted and the + * cast satisfies TypeScript at the injection boundary. + */ +type NcmecDecisionRow = Awaited< + ReturnType +>[number]; + +function makeNcmecDecisionRow(orgId: string): NcmecDecisionRow { + const userItemTypeId = uuidv1(); + const itemId = uuidv1(); + return { + org_id: orgId, + id: uuidv1(), + queue_id: uuidv1(), + reviewer_id: 'reviewer-1', + decision_components: [ + { + type: 'SUBMIT_NCMEC_REPORT', + reportedMedia: [], + reportedMessages: [], + incidentType: + 'Child Pornography (possession, manufacture, and distribution)', + }, + ], + job_payload: { + id: uuidv1(), + orgId, + createdAt: new Date(), + policyIds: [], + payload: { + kind: 'NCMEC', + reportHistory: [], + allMediaItems: [], + item: instantiateOpaqueType({ + submissionId: makeSubmissionId(), + submissionTime: new Date(), + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions + data: {} as NormalizedItemData, + itemTypeIdentifier: { + id: userItemTypeId, + version: new Date().toISOString(), + schemaVariant: 'original', + }, + creator: { + id: itemId, + typeId: userItemTypeId, + }, + itemId, + }), + enqueueSourceInfo: { kind: 'REPORT' }, + }, + // The full `ManualReviewJob` union has many fields the retry job never + // reads; cast through `unknown` to avoid mirroring the entire type. + } as unknown as NcmecDecisionRow['job_payload'], + } as unknown as NcmecDecisionRow; +} + +/** The USER item type returned by `getItemTypeEventuallyConsistent`. Only the + * fields read by the retry job / buildSubmitReportParamsFromDecision are set. */ +function makeUserItemType(id: string) { + return { + id, + kind: 'USER' as const, + name: 'Test User Type', + description: null, + version: new Date().toISOString(), + schemaVariant: 'original' as const, + orgId: 'org-1', + isDefaultUserType: false, + schema: [ + { + name: 'displayName', + type: 'TEXT', + optional: true, + }, + ], + schemaFieldRoles: {}, + }; +} + +/** Builds the 7 dependencies injected into the retry job. Each test overrides + * only what it needs via `overrides`. Methods that should never be called in a + * given test throw, so unexpected calls surface as failures. */ +function makeDeps( + overrides: Partial<{ + submitReport: jest.Mock; + publishActions: jest.Mock; + getNCMECActionsToRunAndPolicies: jest.Mock; + decisions: NcmecDecisionRow[]; + }> = {}, +) { + const ncmecService = { + submitReport: overrides.submitReport ?? jest.fn(async () => 'SUCCESS'), + getUsersWithNcmecDecision: jest.fn(async () => []), + getNcmecErrorsForJobIds: jest.fn(async () => []), + insertOrUpdateNcmecReportError: jest.fn(async () => undefined), + getNCMECActionsToRunAndPolicies: + overrides.getNCMECActionsToRunAndPolicies ?? + jest.fn(async () => undefined), + }; + const manualReviewToolService = { + getNcmecDecisions: jest.fn(async () => overrides.decisions ?? []), + }; + const getItemTypeEventuallyConsistent = jest.fn(async () => + makeUserItemType('user-type-1'), + ); + const actionPublisher = { + publishActions: overrides.publishActions ?? jest.fn(async () => []), + }; + const moderationConfigService = { + getActions: jest.fn(async () => []), + getPolicies: jest.fn(async () => []), + }; + const userManagementService = { + getUsersForOrg: jest.fn(async () => []), + }; + return { + ncmecService, + manualReviewToolService, + getItemTypeEventuallyConsistent, + actionPublisher, + moderationConfigService, + userManagementService, + }; +} + +describe('RetryFailedNcmecDecisionsJob', () => { + const ORG_ID = 'org-1'; + + /** Snapshot of NCMEC_ENV across the suite so each test can mutate it + * freely and we restore the original value in afterEach. */ + let originalNcmecEnv: string | undefined; + + beforeEach(() => { + originalNcmecEnv = process.env.NCMEC_ENV; + }); + afterEach(() => { + if (originalNcmecEnv === undefined) { + delete process.env.NCMEC_ENV; + } else { + process.env.NCMEC_ENV = originalNcmecEnv; + } + }); + + it('passes isTest=true to submitReport when NCMEC_ENV is unset', async () => { + delete process.env.NCMEC_ENV; + const deps = makeDeps({ + decisions: [makeNcmecDecisionRow(ORG_ID)], + }); + const job = makeRetryFailedNcmecDecisionsJob( + jest.fn() as never, // closeSharedResourcesForShutdown (unused by run) + deps.manualReviewToolService as never, + deps.ncmecService as never, + deps.getItemTypeEventuallyConsistent as never, + deps.actionPublisher as never, + deps.moderationConfigService as never, + deps.userManagementService as never, + ); + + await job.run(); + + expect(deps.ncmecService.submitReport).toHaveBeenCalledTimes(1); + const [, isTest] = deps.ncmecService.submitReport.mock.calls[0]; + expect(isTest).toBe(true); + }); + + it('passes isTest=true to submitReport when NCMEC_ENV is "test"', async () => { + process.env.NCMEC_ENV = 'test'; + const deps = makeDeps({ + decisions: [makeNcmecDecisionRow(ORG_ID)], + }); + const job = makeRetryFailedNcmecDecisionsJob( + jest.fn() as never, + deps.manualReviewToolService as never, + deps.ncmecService as never, + deps.getItemTypeEventuallyConsistent as never, + deps.actionPublisher as never, + deps.moderationConfigService as never, + deps.userManagementService as never, + ); + + await job.run(); + + expect(deps.ncmecService.submitReport).toHaveBeenCalledTimes(1); + const [, isTest] = deps.ncmecService.submitReport.mock.calls[0]; + expect(isTest).toBe(true); + }); + + it('passes isTest=false to submitReport when NCMEC_ENV=production', async () => { + process.env.NCMEC_ENV = 'production'; + const deps = makeDeps({ + decisions: [makeNcmecDecisionRow(ORG_ID)], + }); + const job = makeRetryFailedNcmecDecisionsJob( + jest.fn() as never, + deps.manualReviewToolService as never, + deps.ncmecService as never, + deps.getItemTypeEventuallyConsistent as never, + deps.actionPublisher as never, + deps.moderationConfigService as never, + deps.userManagementService as never, + ); + + await job.run(); + + expect(deps.ncmecService.submitReport).toHaveBeenCalledTimes(1); + const [, isTest] = deps.ncmecService.submitReport.mock.calls[0]; + expect(isTest).toBe(false); + }); + + it('does not publish actions when NCMEC_ENV is unset (isTest=true)', async () => { + delete process.env.NCMEC_ENV; + const deps = makeDeps({ + decisions: [makeNcmecDecisionRow(ORG_ID)], + // Simulate an org that has actions configured to run on NCMEC report + // creation. In production mode these would publish; in test mode they + // must be suppressed. + getNCMECActionsToRunAndPolicies: jest.fn(async () => ({ + actionsToRunIds: ['action-1'], + policyIds: ['policy-1'], + })), + }); + const job = makeRetryFailedNcmecDecisionsJob( + jest.fn() as never, + deps.manualReviewToolService as never, + deps.ncmecService as never, + deps.getItemTypeEventuallyConsistent as never, + deps.actionPublisher as never, + deps.moderationConfigService as never, + deps.userManagementService as never, + ); + + await job.run(); + + expect(deps.ncmecService.submitReport).toHaveBeenCalledTimes(1); + expect(deps.actionPublisher.publishActions).not.toHaveBeenCalled(); + }); + + it('publishes actions when NCMEC_ENV=production (isTest=false)', async () => { + process.env.NCMEC_ENV = 'production'; + const deps = makeDeps({ + decisions: [makeNcmecDecisionRow(ORG_ID)], + getNCMECActionsToRunAndPolicies: jest.fn(async () => ({ + actionsToRunIds: ['action-1'], + policyIds: ['policy-1'], + })), + }); + const job = makeRetryFailedNcmecDecisionsJob( + jest.fn() as never, + deps.manualReviewToolService as never, + deps.ncmecService as never, + deps.getItemTypeEventuallyConsistent as never, + deps.actionPublisher as never, + deps.moderationConfigService as never, + deps.userManagementService as never, + ); + + await job.run(); + + expect(deps.ncmecService.submitReport).toHaveBeenCalledTimes(1); + expect(deps.actionPublisher.publishActions).toHaveBeenCalledTimes(1); + }); +}); diff --git a/server/workers_jobs/RetryFailedNcmecDecisionsJob.ts b/server/workers_jobs/RetryFailedNcmecDecisionsJob.ts index 18d9df63..0cdb7d23 100644 --- a/server/workers_jobs/RetryFailedNcmecDecisionsJob.ts +++ b/server/workers_jobs/RetryFailedNcmecDecisionsJob.ts @@ -118,9 +118,10 @@ export default inject( getItemTypeEventuallyConsistent, }); submitReportInvoked = true; + const isTest = process.env.NCMEC_ENV !== 'production'; const reportResult = await ncmecService.submitReport( reportParams, - false, + isTest, ); if ( reportResult === 'UNSUPPORTED_ORG' || @@ -133,7 +134,8 @@ export default inject( await ncmecService.getNCMECActionsToRunAndPolicies(orgId); if ( actionAndPolicy != null && - actionAndPolicy.actionsToRunIds != null + actionAndPolicy.actionsToRunIds != null && + !isTest ) { const actions = await moderationConfigService.getActions({ orgId, From c724aaaced210d8f00b6a764775c666b044879e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tao=20Bojl=C3=A9n?= Date: Tue, 4 Aug 2026 00:48:01 +0100 Subject: [PATCH 48/57] Clarify npm rules in AGENTS.md (#953) * Clarify approval requirements for routine commands Co-Authored-By: OpenAI Codex * Refine routine command approval guidance Co-Authored-By: OpenAI Codex --------- Co-authored-by: OpenAI Codex --- AGENTS.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index ea82166a..c3906e60 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -200,6 +200,8 @@ Two things differ from a local dev setup: ## Human-approval-required actions +Routine local setup and verification commands, including `npm ci` and existing build/test/lint/format/check scripts, do not require approval; the gates below apply to the changes being made, not merely to running commands. + Stop and get explicit human approval before: - Changing license headers, copyright notices, or any legal text (including `LICENSE`). @@ -208,7 +210,7 @@ Stop and get explicit human approval before: - Deleting or renaming an existing GraphQL type or field — this breaks cached Apollo client state and any downstream consumer. Additive changes are usually safe; removals need a migration plan. - Rewiring `server/iocContainer` in a way that changes service lifecycles or startup order — cascading effects on tests and boot. - Auth, session, or request middleware (under `server/api.ts`) — security-sensitive; prefer a small, reviewable PR with explicit callouts. -- Adding, removing, or upgrading any library or package (including transitive dependencies in `package-lock.json`) — confirm licenses are compatible with Apache 2.0 and that there are no known CVEs. +- Adding, removing, or upgrading any dependency (including transitive dependencies in `package-lock.json`) — confirm licenses are compatible with Apache 2.0 and that there are no known CVEs. - Multi-thousand-line diffs — ROOST policy is that reviewers can digest the change. Split into reviewable PRs; regenerated codegen and lockfile bumps are the only exceptions. ## Commit attribution From 09cf03a11340851018ea3a60711a8c8e123e946c Mon Sep 17 00:00:00 2001 From: Caleb McQuaid Date: Mon, 3 Aug 2026 19:49:45 -0400 Subject: [PATCH 49/57] fix: remove table constraint for NCMEC non-media submissions (#871) --- ...03.50.47.allow_text_only_ncmec_reports.sql | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 db/src/scripts/api-server-pg/2026.06.30T03.50.47.allow_text_only_ncmec_reports.sql diff --git a/db/src/scripts/api-server-pg/2026.06.30T03.50.47.allow_text_only_ncmec_reports.sql b/db/src/scripts/api-server-pg/2026.06.30T03.50.47.allow_text_only_ncmec_reports.sql new file mode 100644 index 00000000..7f241467 --- /dev/null +++ b/db/src/scripts/api-server-pg/2026.06.30T03.50.47.allow_text_only_ncmec_reports.sql @@ -0,0 +1,30 @@ +-- The existing `reported_media_check_non_empty` CHECK on ncmec_reporting.ncmec_reports +-- requires >= 1 media item, which blocks storing legitimate text-only reports +-- even after the application-side media gates were removed (#661). +-- +-- We want to replace it with a constraint that matches the new logic. A +-- report must carry media OR messages (but there must be one). `array_length` of an +-- empty/NULL array is NULL, so coalesce to 0 before comparing. The column stays +-- `jsonb[] NOT NULL`, so rows still cannot be NULL. +-- +-- Safe for existing data: every current row satisfied the old "media non-empty" +-- constraint, so it satisfies "media OR messages non-empty". +-- +-- Rollback caveat: reverting to the media-only constraint will FAIL once any +-- text-only (empty reported_media) row exists. Reverting is only clean +-- before such rows are written; afterward it requires removing/backfilling +-- those rows, and deleting NCMEC report records has compliance implications. +-- Treat this as effectively forward-only. + +BEGIN; + +ALTER TABLE ncmec_reporting.ncmec_reports + DROP CONSTRAINT IF EXISTS reported_media_check_non_empty; + +ALTER TABLE ncmec_reporting.ncmec_reports + ADD CONSTRAINT reported_media_or_messages_non_empty CHECK ( + coalesce(array_length(reported_media, 1), 0) > 0 + OR coalesce(array_length(reported_messages, 1), 0) > 0 + ); + +COMMIT; From 3b706ecd4ae2cbc6596590bd283f6694559f8960 Mon Sep 17 00:00:00 2001 From: Mark Reitblatt Date: Tue, 4 Aug 2026 16:43:27 -0700 Subject: [PATCH 50/57] Refuse queue deletion when routing rules still reference it (#808) * [Fix] Refuse queue deletion when routing rules still reference it Fixes issue #738 (design #1). Changes the FK constraints on routing_rules.destination_queue_id and appeals_routing_rules.destination_queue_id from ON DELETE CASCADE to ON DELETE RESTRICT. The service now catches the resulting FK violation and throws QueueHasDependentRoutingRulesError, naming the blocking rules, instead of silently cascade-deleting them and breaking routing. The client's previously silent onError handler is replaced with a modal that surfaces the server's error message so the user knows which rules to update before retrying. Regression tests cover both routing_rules and appeals_routing_rules. Co-Authored-By: Claude Sonnet 4.6 * [Queue Delete behavior] Put blocking rules on their own line, indent, and link to rules page * Remove accidentally included test code * [Fix] Update test helper to delete routing rules before queue deletion deleteManualReviewQueueForTestsDO_NOT_USE was written when routing_rules had CASCADE FKs on destination_queue_id. Now that the FK is RESTRICT, the helper must explicitly delete referencing routing rules and appeals routing rules before removing the queue, or the DB rejects the delete. Co-Authored-By: Claude Sonnet 4.6 * [Fix] Address PR reviewer feedback on queue delete behavior - Move queue.obliterate() after DB transaction so Redis jobs aren't destroyed if deletion is blocked by FK constraints - Gate FK error mapping on specific constraint names to avoid swallowing unrelated 23503 errors from future RESTRICT FKs - Add org_id filter to routing rule deletes in test helper - Validate JSON.parse result is string[] before assigning to ruleNames - Use index as React key to avoid collisions on duplicate rule names Co-Authored-By: Claude Sonnet 4.6 * [Fix] Resolve CI failures after rebase on main Rename the routing-rules migration to a fresh timestamp since main gained a later migration (add_sepia) during the rebase, and dedupe the two new QueueOperations.test.ts cases behind a shared helper to get the file back under the 500-line lint limit. Co-Authored-By: Claude Sonnet 5 * [Fix] Address CodeRabbit feedback: log obliterate() failures after commit If queue.obliterate() throws after the DB delete has already committed, a retry would see numDeletedRows === 0n, skip obliterate() entirely, and silently leave orphaned Bull/Redis data behind. Wrap it in try/catch and surface the failure to the active tracing span, matching the best-effort cleanup pattern used elsewhere (e.g. UserApi.logout). Co-Authored-By: Claude Sonnet 5 * address code review * make lint happy --------- Co-authored-by: Claude Sonnet 4.6 Co-authored-by: Juan Mrad --- .../investigation/ItemInvestigation.tsx | 13 +-- .../mrt/ManualReviewQueuesDashboard.tsx | 68 ++++++++++++- ....32.restrict_routing_rules_queue_fkeys.sql | 18 ++++ .../manualReviewToolService.ts | 1 + .../modules/JobRouting.test.ts | 7 +- .../modules/QueueOperations.test.ts | 82 +++++++++++++-- .../modules/QueueOperations.ts | 99 +++++++++++++++++-- 7 files changed, 257 insertions(+), 31 deletions(-) create mode 100644 db/src/scripts/api-server-pg/2026.07.13T21.56.32.restrict_routing_rules_queue_fkeys.sql diff --git a/client/src/webpages/dashboard/investigation/ItemInvestigation.tsx b/client/src/webpages/dashboard/investigation/ItemInvestigation.tsx index 416c1fd8..c0ae2937 100644 --- a/client/src/webpages/dashboard/investigation/ItemInvestigation.tsx +++ b/client/src/webpages/dashboard/investigation/ItemInvestigation.tsx @@ -11,6 +11,7 @@ import ItemAction from '@/components/ItemAction'; import { GQLItemHistoryResult, GQLItemType, + GQLSchemaFieldRoles, GQLThreadItem, GQLUserItem, useGQLGetItemsWithIdLazyQuery, @@ -401,16 +402,8 @@ export default function ItemInvestigation(props: { // shadowing it. const derivedIpAddress = (() => { try { - return getFieldValueForRole( - { - // The GraphQL result is an item-type union, but the generic helper - // requires one compatible schema-role type at this call site. - // eslint-disable-next-line custom-rules/no-casting-in-getFieldValueForRole, @typescript-eslint/no-unnecessary-type-assertion - type: item.type as Parameters< - typeof getFieldValueForRole - >[0]['type'], - data: item.data, - }, + return getFieldValueForRole( + { type: item.type, data: item.data }, 'ipAddress', ); } catch { diff --git a/client/src/webpages/dashboard/mrt/ManualReviewQueuesDashboard.tsx b/client/src/webpages/dashboard/mrt/ManualReviewQueuesDashboard.tsx index 2073b9c2..8ac60d34 100644 --- a/client/src/webpages/dashboard/mrt/ManualReviewQueuesDashboard.tsx +++ b/client/src/webpages/dashboard/mrt/ManualReviewQueuesDashboard.tsx @@ -6,7 +6,14 @@ import { gql } from '@apollo/client'; import Button from 'antd/lib/button'; import Checkbox from 'antd/lib/checkbox'; import Input from 'antd/lib/input'; -import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { + Fragment, + useCallback, + useEffect, + useMemo, + useRef, + useState, +} from 'react'; import { Helmet } from 'react-helmet-async'; import { Link, useNavigate } from 'react-router-dom'; @@ -207,8 +214,29 @@ export default function ManualReviewQueuesDashboard() { fetchPolicy: 'no-cache', pollInterval: 5000, }); + const [deleteError, setDeleteError] = useState<{ + message: string; + ruleNames: string[]; + } | null>(null); const [deleteReviewQueue] = useGQLDeleteManualReviewQueueMutation({ - onError: () => {}, + onError: (error) => { + const gqlError = error.graphQLErrors[0]; + const message = gqlError?.message ?? 'Failed to delete queue.'; + const rawDetail = gqlError?.extensions?.['detail']; + let ruleNames: string[] = []; + if (typeof rawDetail === 'string') { + try { + const parsed: unknown = JSON.parse(rawDetail); + if ( + Array.isArray(parsed) && + parsed.every((n): n is string => typeof n === 'string') + ) { + ruleNames = parsed; + } + } catch {} + } + setDeleteError({ message, ruleNames }); + }, onCompleted: async () => refetch(), }); const [addFavoriteMRTQueue] = useGQLAddFavoriteMrtQueueMutation({ @@ -442,6 +470,41 @@ export default function ManualReviewQueuesDashboard() { ); + const deleteErrorModal = ( + setDeleteError(null), + type: 'primary', + }, + ]} + onClose={() => setDeleteError(null)} + > + {deleteError?.ruleNames && deleteError.ruleNames.length > 0 ? ( +
+

+ This queue cannot be deleted because it is used by the following + routing rules: +

+

+ {deleteError.ruleNames.map((name, i) => ( + + {i > 0 && ', '} + {name} + + ))} +

+

Update or delete those rules first.

+
+ ) : ( +

{deleteError?.message}

+ )} +
+ ); + const onDeleteReviewQueue = (id: string) => { deleteReviewQueue({ variables: { id }, @@ -916,6 +979,7 @@ export default function ManualReviewQueuesDashboard() { /> } {deleteModal} + {deleteErrorModal} {deleteAllJobsModal}
); diff --git a/db/src/scripts/api-server-pg/2026.07.13T21.56.32.restrict_routing_rules_queue_fkeys.sql b/db/src/scripts/api-server-pg/2026.07.13T21.56.32.restrict_routing_rules_queue_fkeys.sql new file mode 100644 index 00000000..b1b88797 --- /dev/null +++ b/db/src/scripts/api-server-pg/2026.07.13T21.56.32.restrict_routing_rules_queue_fkeys.sql @@ -0,0 +1,18 @@ +-- Change routing_rules and appeals_routing_rules destination_queue_id FK from +-- CASCADE to RESTRICT so that deleting a queue that is still referenced by a +-- routing rule is rejected at the DB level (rather than silently cascade- +-- deleting the rule and breaking routing for the org). + +ALTER TABLE manual_review_tool.routing_rules + DROP CONSTRAINT routing_rules_destination_queue_id_fkey, + ADD CONSTRAINT routing_rules_destination_queue_id_fkey + FOREIGN KEY (destination_queue_id) + REFERENCES manual_review_tool.manual_review_queues(id) + ON DELETE RESTRICT; + +ALTER TABLE manual_review_tool.appeals_routing_rules + DROP CONSTRAINT appeals_routing_rules_destination_queue_id_fkey, + ADD CONSTRAINT appeals_routing_rules_destination_queue_id_fkey + FOREIGN KEY (destination_queue_id) + REFERENCES manual_review_tool.manual_review_queues(id) + ON DELETE RESTRICT; diff --git a/server/services/manualReviewToolService/manualReviewToolService.ts b/server/services/manualReviewToolService/manualReviewToolService.ts index ad6f2e48..d8a733ab 100644 --- a/server/services/manualReviewToolService/manualReviewToolService.ts +++ b/server/services/manualReviewToolService/manualReviewToolService.ts @@ -325,6 +325,7 @@ export class ManualReviewToolService { pgQueryReadReplica, moderationConfigService, redis, + tracer, ); this.jobEnrichment = new JobEnrichment( partialItemsService, diff --git a/server/services/manualReviewToolService/modules/JobRouting.test.ts b/server/services/manualReviewToolService/modules/JobRouting.test.ts index 6b419307..ff804096 100644 --- a/server/services/manualReviewToolService/modules/JobRouting.test.ts +++ b/server/services/manualReviewToolService/modules/JobRouting.test.ts @@ -553,10 +553,9 @@ describe('JobRouting tests', () => { orgId: org.id, queueId: defaultQueue.id, }); - // Deleting the queue will also delete the routing rule, via cascading - // delete. However, the old routing rules will still be in the MRT Service's - // cache, meaning that running the rules will now point to a queue that - // doesn't exist. In this case, it should fall back to the default. + // deleteManualReviewQueueForTestsDO_NOT_USE removes any routing rules that + // reference the queue (RESTRICT FK) before deleting it. After deletion, + // enqueue should find no matching rules and fall back to the default queue. await manualReviewToolService.deleteManualReviewQueueForTestsDO_NOT_USE( org.id, anotherQueue.id, diff --git a/server/services/manualReviewToolService/modules/QueueOperations.test.ts b/server/services/manualReviewToolService/modules/QueueOperations.test.ts index 915c9672..b0422f29 100644 --- a/server/services/manualReviewToolService/modules/QueueOperations.test.ts +++ b/server/services/manualReviewToolService/modules/QueueOperations.test.ts @@ -1,5 +1,6 @@ import fc from 'fast-check'; import { uid } from 'uid'; +import { v1 as uuidv1 } from 'uuid'; import createActions from '../../../test/fixtureHelpers/createActions.js'; import createContentItemTypes from '../../../test/fixtureHelpers/createContentItemTypes.js'; @@ -74,8 +75,10 @@ describe('QueueOperations', () => { return { org, + user, actions, queue, + kyselyPg: deps.KyselyPg, mrtService: deps.ManualReviewToolService, }; }); @@ -90,7 +93,6 @@ describe('QueueOperations', () => { expect(hiddenActions.length).toEqual(0); }, ); - testWithQueueAndActions()( 'Test hiding an action', async ({ org, queue, mrtService, actions }) => { @@ -111,7 +113,6 @@ describe('QueueOperations', () => { expect(hiddenActions[0]).toEqual(actionToHide.id); }, ); - testWithQueueAndActions()( 'Test unhiding an action', async ({ org, queue, mrtService, actions }) => { @@ -140,7 +141,6 @@ describe('QueueOperations', () => { expect(hiddenActions).not.toContain(actionToUnhide.id); }, ); - testWithQueueAndActions()( 'Test hiding some actions and unhiding some others', async ({ org, queue, mrtService, actions }) => { @@ -254,7 +254,6 @@ describe('QueueOperations', () => { ).resolves.toBeUndefined(); }, ); - const testWithTwoOrgs = () => makeTransactionalTestWithFixture(async ({ deps }) => { const buildOrg = async () => { @@ -301,7 +300,6 @@ describe('QueueOperations', () => { expect(viewers.map((v) => v.userId)).not.toContain(attacker.user.id); }, ); - testWithTwoOrgs()( 'addAccessibleQueuesForUser must not grant access for a user in a different org', async ({ attacker, victim, mrtService }) => { @@ -321,7 +319,6 @@ describe('QueueOperations', () => { expect(viewers.map((v) => v.userId)).not.toContain(victim.user.id); }, ); - testWithTwoOrgs()( 'removeAccessibleQueuesForUser must not revoke access for a queue in a different org', async ({ attacker, victim, mrtService }) => { @@ -347,7 +344,6 @@ describe('QueueOperations', () => { expect(viewers.map((v) => v.userId)).toContain(victim.user.id); }, ); - testWithTwoOrgs()( 'removeAccessibleQueuesForUser must not revoke access for a user in a different org', async ({ attacker, victim, mrtService }) => { @@ -429,4 +425,76 @@ describe('QueueOperations', () => { expect(viewers.map((v) => v.userId)).not.toContain(victim.user.id); }, ); + + // Regression: RESTRICT FK must block queue deletion when routing rules reference it. + type QueueFixture = Parameters< + Parameters>[1] + >[0]; + type RuleTable = + | 'manual_review_tool.routing_rules' + | 'manual_review_tool.appeals_routing_rules'; + + const expectDeletionBlockedByRoutingRule = async ( + { org, user, mrtService, kyselyPg }: QueueFixture, + table: RuleTable, + ruleName: string, + ) => { + const secondQueue = await mrtService.createManualReviewQueue({ + name: `delete-test-queue-${uid()}`, + description: null, + userIds: [user.id], + hiddenActionIds: [], + isAppealsQueue: false, + invokedBy: { + userId: user.id, + permissions: [UserPermission.EDIT_MRT_QUEUES], + orgId: org.id, + }, + }); + await kyselyPg + .insertInto(table) + .values({ + id: uuidv1(), + org_id: org.id, + name: ruleName, + description: null, + status: 'LIVE', + condition_set: { conditions: [], conjunction: 'AND' }, + destination_queue_id: secondQueue.id, + creator_id: user.id, + sequence_number: 99, + }) + .execute(); + await expect( + mrtService.deleteManualReviewQueue(org.id, secondQueue.id), + ).rejects.toMatchObject({ name: 'QueueHasDependentRoutingRulesError' }); + await kyselyPg + .deleteFrom(table) + .where('destination_queue_id', '=', secondQueue.id) + .execute(); + await mrtService.deleteManualReviewQueueForTestsDO_NOT_USE( + org.id, + secondQueue.id, + ); + }; + + testWithQueueAndActions()( + 'deleteManualReviewQueue rejects when a routing rule references the queue', + async (ctx) => + expectDeletionBlockedByRoutingRule( + ctx, + 'manual_review_tool.routing_rules', + 'block-rule', + ), + ); + + testWithQueueAndActions()( + 'deleteManualReviewQueue rejects when an appeals routing rule references the queue', + async (ctx) => + expectDeletionBlockedByRoutingRule( + ctx, + 'manual_review_tool.appeals_routing_rules', + 'block-appeals-rule', + ), + ); }); diff --git a/server/services/manualReviewToolService/modules/QueueOperations.ts b/server/services/manualReviewToolService/modules/QueueOperations.ts index d4afc212..bc515f5b 100644 --- a/server/services/manualReviewToolService/modules/QueueOperations.ts +++ b/server/services/manualReviewToolService/modules/QueueOperations.ts @@ -15,6 +15,7 @@ import { filterNullOrUndefined } from '../../../utils/collections.js'; import { b64UrlDecode, b64UrlEncode, + jsonStringify, type B64UrlOf, } from '../../../utils/encoding.js'; import { @@ -23,7 +24,10 @@ import { makeUnauthorizedError, type ErrorInstanceData, } from '../../../utils/errors.js'; -import { isUniqueViolationError } from '../../../utils/kysely.js'; +import { + isForeignKeyViolationError, + isUniqueViolationError, +} from '../../../utils/kysely.js'; import { makeKyselyTransactionWithRetry, type KyselyTransactionWithRetry, @@ -105,7 +109,8 @@ export type QueueOperationsErrorType = | 'DeleteAllJobsUnauthorizedError' | 'QueueDoesNotExistError' | 'UnableToDeleteDefaultQueueError' - | 'AccessibleQueueNotInOrgError'; + | 'AccessibleQueueNotInOrgError' + | 'QueueHasDependentRoutingRulesError'; // Compound identifier for a queue. orgId is needed for security, but also // because queues are/will be actually sharded across redis instances for @@ -162,6 +167,7 @@ export default class QueueOperations { private readonly pgQueryReadReplica: Kysely, private readonly moderationConfigService: Dependencies['ModerationConfigService'], redis: RedisConnection, + private readonly tracer: Dependencies['Tracer'], ) { this.transactionWithRetry = makeKyselyTransactionWithRetry(this.pgQuery); // Reassingment here is a hack to work around TS syntax limitations @@ -440,10 +446,9 @@ export default class QueueOperations { } const queue = await this.getOrCreateBullQueue({ orgId, queueId }); - await queue.obliterate({ force: true }); - - const numDeletedRows = await this.transactionWithRetry( - async (transaction) => { + let numDeletedRows: bigint; + try { + numDeletedRows = await this.transactionWithRetry(async (transaction) => { // Delete the queue scoped by org first. If it doesn't belong to the // caller's org, no rows are touched and we bail before deleting any // join rows. `users_and_accessible_queues` has no `org_id` column, @@ -465,8 +470,55 @@ export default class QueueOperations { .execute(); return queueDelete.numDeletedRows; - }, - ); + }); + } catch (e) { + const constraint = (e as { constraint?: string }).constraint; + if ( + isForeignKeyViolationError(e) && + (constraint === 'routing_rules_destination_queue_id_fkey' || + constraint === 'appeals_routing_rules_destination_queue_id_fkey') + ) { + // routing_rules and appeals_routing_rules have RESTRICT FKs to this + // queue. Query for their names so the error message is actionable. + const [routingRules, appealsRoutingRules] = await Promise.all([ + this.pgQuery + .selectFrom('manual_review_tool.routing_rules') + .select(['name']) + .where('destination_queue_id', '=', queueId) + .where('org_id', '=', orgId) + .execute(), + this.pgQuery + .selectFrom('manual_review_tool.appeals_routing_rules') + .select(['name']) + .where('destination_queue_id', '=', queueId) + .where('org_id', '=', orgId) + .execute(), + ]); + const ruleNames = [ + ...routingRules.map((r) => r.name), + ...appealsRoutingRules.map((r) => r.name), + ]; + throw makeQueueHasDependentRoutingRulesError(ruleNames, { + shouldErrorSpan: false, + }); + } + throw e; + } + + if (numDeletedRows === 1n) { + try { + await queue.obliterate({ force: true }); + } catch (e) { + // The DB row is already gone at this point, so a retry would see + // numDeletedRows === 0n and skip obliterate() entirely, silently + // leaving orphaned Bull/Redis data behind. Best-effort cleanup: + // surface the failure to the active tracing span (mirrors the + // pattern used elsewhere, e.g. `UserApi.logout`) so ops can run + // `server/bin/recover-mrt-queue.ts`, but still report success since + // the DB delete itself succeeded. + this.tracer.logActiveSpanFailedIfAny(e); + } + } return numDeletedRows === 1n; } @@ -481,8 +533,23 @@ export default class QueueOperations { // See `deleteManualReviewQueue` for why this is serialized + ownership- // checked. Same pattern, just without the default-queue guard. + // + // routing_rules and appeals_routing_rules have RESTRICT FKs to this queue, + // so we must delete any referencing rules before deleting the queue. const numDeletedRows = await this.transactionWithRetry( async (transaction) => { + await transaction + .deleteFrom('manual_review_tool.routing_rules') + .where('destination_queue_id', '=', queueId) + .where('org_id', '=', orgId) + .execute(); + + await transaction + .deleteFrom('manual_review_tool.appeals_routing_rules') + .where('destination_queue_id', '=', queueId) + .where('org_id', '=', orgId) + .execute(); + const queueDelete = await transaction .deleteFrom('manual_review_tool.manual_review_queues') .where('id', '=', queueId) @@ -2047,3 +2114,19 @@ export const makeManualReviewQueueNameExistsError = (data: ErrorInstanceData) => name: 'ManualReviewQueueNameExistsError', ...data, }); + +export const makeQueueHasDependentRoutingRulesError = ( + ruleNames: string[], + data: ErrorInstanceData, +) => + new CoopError({ + status: 409, + type: [ErrorType.Conflict], + title: + ruleNames.length > 0 + ? `This queue cannot be deleted because it is used by the following routing rules:\n${ruleNames.join(', ')}\nUpdate or delete those rules first.` + : 'This queue cannot be deleted because it is still referenced by one or more routing rules. Update or delete those rules first.', + detail: ruleNames.length > 0 ? jsonStringify(ruleNames) : undefined, + name: 'QueueHasDependentRoutingRulesError', + ...data, + }); From 0aeb7ebc2cd85c064a7bd57be591a9a561d5ec99 Mon Sep 17 00:00:00 2001 From: juliet Date: Wed, 5 Aug 2026 08:14:23 -0500 Subject: [PATCH 51/57] Add self-harm/intent + self-harm/instructions OpenAI signals (#535) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add self-harm/intent and self-harm/instructions signals Round out the omni-moderation self-harm coverage that the prior commit left lopsided. OpenAI scores both subcategories for text *and* image inputs; we had neither. New signal classes (4): - OpenAiSelfHarmIntentTextSignal / OpenAiSelfHarmIntentImageSignal - OpenAiSelfHarmInstructionsTextSignal / OpenAiSelfHarmInstructionsImageSignal Plus matching SignalType enum entries, integrationForSignalType cases, SignalArgsByType / RuntimeSignalArgsByType entries, IoC registration, and the two new category names in OpenAiModelName + OpenAiImageModelName. Co-Authored-By: Claude Opus 4.7 (1M context) * Expose self-harm/intent + instructions signals to GraphQL + client Same shape as the previous fix on PR #534: the four new self-harm subcategory signal types need to round-trip through both hand-maintained mirrors or the dashboard can't see them. - server/graphql/modules/signal.ts: add the four OPEN_AI_SELF_HARM_{INTENT,INSTRUCTIONS}_{TEXT,IMAGE}_MODEL types to the SDL enum. - client/src/models/signal.ts: add the same four to the OpenAi case in integrationForSignalType. - Regenerate codegen. The coverage test added in PR #534 catches this regression class on new SignalType additions; it now exercises 33 enum values (was 29). Co-Authored-By: Claude Opus 4.7 (1M context) * Add docstrings to self-harm/intent + instructions signal classes Matches the docstring style applied to the base PR's image signals. Raises this PR's docstring coverage above CodeRabbit's 80% pre-merge threshold by documenting each of the four new signal classes (self-harm/intent text+image, self-harm/instructions text+image) and their run() methods. No behavior change. Co-Authored-By: Claude Opus 4.7 (1M context) * Apply factory pattern to self-harm subcategory signals Mirrors the cleanup from PR #534: the 4 self-harm subcategory signal classes (2 text + 2 image) collapse to ~20-line factory calls instead of ~110-line class definitions each. - Adds `makeOpenAiTextModerationSignal` alongside the existing image factory introduced in #534. - Both factories now share a single internal `makeOpenAiModerationSignal` parameterized by input scalar + run impl, so the class body lives in one place (was duplicated twice in #534's first cut). - Factories moved into `openAiModerationSignalFactory.ts` — keeps `openAIModerationUtils.ts` under the 500-line lint limit (the second factory pushed it to 510) and organizes the file boundary cleanly: utils = lib functions, factory = class generator. The 4 self-harm subcategory signal files (text + image variants for `self-harm/intent` and `self-harm/instructions`) now just import the appropriate factory and pass a config object. Co-Authored-By: Claude Opus 4.7 (1M context) * fix bad import --------- Co-authored-by: Claude Opus 4.7 (1M context) Co-authored-by: Juan Mrad --- client/src/graphql/generated.ts | 6 + client/src/models/signal.ts | 4 + server/graphql/generated.ts | 6 + server/graphql/modules/signal.ts | 4 + .../helpers/instantiateBuiltInSignals.ts | 24 +++ .../OpenAiGraphicViolenceImageSignal.ts | 2 +- .../moderation/OpenAiSelfHarmImageSignal.ts | 2 +- .../OpenAiSelfHarmInstructionsImageSignal.ts | 19 ++ .../OpenAiSelfHarmInstructionsTextSignal.ts | 19 ++ .../OpenAiSelfHarmIntentImageSignal.ts | 19 ++ .../OpenAiSelfHarmIntentTextSignal.ts | 19 ++ .../moderation/OpenAiSexualImageSignal.ts | 2 +- .../moderation/OpenAiViolenceImageSignal.ts | 2 +- .../moderation/openAIModerationUtils.ts | 124 +----------- .../openAiModerationSignalFactory.ts | 191 ++++++++++++++++++ .../signalsService/types/SignalArgsByType.ts | 8 + .../signalsService/types/SignalType.ts | 8 + 17 files changed, 340 insertions(+), 119 deletions(-) create mode 100644 server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiSelfHarmInstructionsImageSignal.ts create mode 100644 server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiSelfHarmInstructionsTextSignal.ts create mode 100644 server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiSelfHarmIntentImageSignal.ts create mode 100644 server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiSelfHarmIntentTextSignal.ts create mode 100644 server/services/signalsService/signals/third_party_signals/open_ai/moderation/openAiModerationSignalFactory.ts diff --git a/client/src/graphql/generated.ts b/client/src/graphql/generated.ts index 6c530df6..58217bef 100644 --- a/client/src/graphql/generated.ts +++ b/client/src/graphql/generated.ts @@ -4450,6 +4450,12 @@ export const GQLSignalType = { OpenAiHateTextModel: 'OPEN_AI_HATE_TEXT_MODEL', OpenAiHateThreateningTextModel: 'OPEN_AI_HATE_THREATENING_TEXT_MODEL', OpenAiSelfHarmImageModel: 'OPEN_AI_SELF_HARM_IMAGE_MODEL', + OpenAiSelfHarmInstructionsImageModel: + 'OPEN_AI_SELF_HARM_INSTRUCTIONS_IMAGE_MODEL', + OpenAiSelfHarmInstructionsTextModel: + 'OPEN_AI_SELF_HARM_INSTRUCTIONS_TEXT_MODEL', + OpenAiSelfHarmIntentImageModel: 'OPEN_AI_SELF_HARM_INTENT_IMAGE_MODEL', + OpenAiSelfHarmIntentTextModel: 'OPEN_AI_SELF_HARM_INTENT_TEXT_MODEL', OpenAiSelfHarmTextModel: 'OPEN_AI_SELF_HARM_TEXT_MODEL', OpenAiSexualImageModel: 'OPEN_AI_SEXUAL_IMAGE_MODEL', OpenAiSexualMinorsTextModel: 'OPEN_AI_SEXUAL_MINORS_TEXT_MODEL', diff --git a/client/src/models/signal.ts b/client/src/models/signal.ts index 24f06f66..5c74378a 100644 --- a/client/src/models/signal.ts +++ b/client/src/models/signal.ts @@ -29,6 +29,10 @@ export function integrationForSignalType(type: string) { case 'OPEN_AI_HATE_TEXT_MODEL': case 'OPEN_AI_HATE_THREATENING_TEXT_MODEL': case 'OPEN_AI_SELF_HARM_IMAGE_MODEL': + case 'OPEN_AI_SELF_HARM_INSTRUCTIONS_IMAGE_MODEL': + case 'OPEN_AI_SELF_HARM_INSTRUCTIONS_TEXT_MODEL': + case 'OPEN_AI_SELF_HARM_INTENT_IMAGE_MODEL': + case 'OPEN_AI_SELF_HARM_INTENT_TEXT_MODEL': case 'OPEN_AI_SELF_HARM_TEXT_MODEL': case 'OPEN_AI_SEXUAL_IMAGE_MODEL': case 'OPEN_AI_SEXUAL_MINORS_TEXT_MODEL': diff --git a/server/graphql/generated.ts b/server/graphql/generated.ts index 178d0f82..12d6be11 100644 --- a/server/graphql/generated.ts +++ b/server/graphql/generated.ts @@ -4518,6 +4518,12 @@ export const GQLSignalType = { OpenAiHateTextModel: 'OPEN_AI_HATE_TEXT_MODEL', OpenAiHateThreateningTextModel: 'OPEN_AI_HATE_THREATENING_TEXT_MODEL', OpenAiSelfHarmImageModel: 'OPEN_AI_SELF_HARM_IMAGE_MODEL', + OpenAiSelfHarmInstructionsImageModel: + 'OPEN_AI_SELF_HARM_INSTRUCTIONS_IMAGE_MODEL', + OpenAiSelfHarmInstructionsTextModel: + 'OPEN_AI_SELF_HARM_INSTRUCTIONS_TEXT_MODEL', + OpenAiSelfHarmIntentImageModel: 'OPEN_AI_SELF_HARM_INTENT_IMAGE_MODEL', + OpenAiSelfHarmIntentTextModel: 'OPEN_AI_SELF_HARM_INTENT_TEXT_MODEL', OpenAiSelfHarmTextModel: 'OPEN_AI_SELF_HARM_TEXT_MODEL', OpenAiSexualImageModel: 'OPEN_AI_SEXUAL_IMAGE_MODEL', OpenAiSexualMinorsTextModel: 'OPEN_AI_SEXUAL_MINORS_TEXT_MODEL', diff --git a/server/graphql/modules/signal.ts b/server/graphql/modules/signal.ts index 060d0c35..a6aae1cf 100644 --- a/server/graphql/modules/signal.ts +++ b/server/graphql/modules/signal.ts @@ -104,6 +104,10 @@ const typeDefs = /* GraphQL */ ` OPEN_AI_HATE_TEXT_MODEL OPEN_AI_HATE_THREATENING_TEXT_MODEL OPEN_AI_SELF_HARM_IMAGE_MODEL + OPEN_AI_SELF_HARM_INSTRUCTIONS_IMAGE_MODEL + OPEN_AI_SELF_HARM_INSTRUCTIONS_TEXT_MODEL + OPEN_AI_SELF_HARM_INTENT_IMAGE_MODEL + OPEN_AI_SELF_HARM_INTENT_TEXT_MODEL OPEN_AI_SELF_HARM_TEXT_MODEL OPEN_AI_SEXUAL_IMAGE_MODEL OPEN_AI_SEXUAL_MINORS_TEXT_MODEL diff --git a/server/services/signalsService/helpers/instantiateBuiltInSignals.ts b/server/services/signalsService/helpers/instantiateBuiltInSignals.ts index d7a67280..ec236dd8 100644 --- a/server/services/signalsService/helpers/instantiateBuiltInSignals.ts +++ b/server/services/signalsService/helpers/instantiateBuiltInSignals.ts @@ -28,6 +28,10 @@ import OpenAiGraphicViolenceTextSignal from '../signals/third_party_signals/open import OpenAiHateTextSignal from '../signals/third_party_signals/open_ai/moderation/OpenAiHateTextSignal.js'; import OpenAiHateThreateningTextSignal from '../signals/third_party_signals/open_ai/moderation/OpenAiHateThreateningTextSignal.js'; import OpenAiSelfHarmImageSignal from '../signals/third_party_signals/open_ai/moderation/OpenAiSelfHarmImageSignal.js'; +import OpenAiSelfHarmInstructionsImageSignal from '../signals/third_party_signals/open_ai/moderation/OpenAiSelfHarmInstructionsImageSignal.js'; +import OpenAiSelfHarmInstructionsTextSignal from '../signals/third_party_signals/open_ai/moderation/OpenAiSelfHarmInstructionsTextSignal.js'; +import OpenAiSelfHarmIntentImageSignal from '../signals/third_party_signals/open_ai/moderation/OpenAiSelfHarmIntentImageSignal.js'; +import OpenAiSelfHarmIntentTextSignal from '../signals/third_party_signals/open_ai/moderation/OpenAiSelfHarmIntentTextSignal.js'; import OpenAiSelfHarmTextSignal from '../signals/third_party_signals/open_ai/moderation/OpenAiSelfHarmTextSignal.js'; import OpenAiSexualImageSignal from '../signals/third_party_signals/open_ai/moderation/OpenAiSexualImageSignal.js'; import OpenAiSexualMinorsTextSignal from '../signals/third_party_signals/open_ai/moderation/OpenAiSexualMinorsTextSignal.js'; @@ -108,6 +112,26 @@ export function instantiateBuiltInSignals( credentialGetters.OPEN_AI, getOpenAiScores, ), + [SignalType.OPEN_AI_SELF_HARM_INSTRUCTIONS_IMAGE_MODEL]: + new OpenAiSelfHarmInstructionsImageSignal( + credentialGetters.OPEN_AI, + getOpenAiScores, + ), + [SignalType.OPEN_AI_SELF_HARM_INSTRUCTIONS_TEXT_MODEL]: + new OpenAiSelfHarmInstructionsTextSignal( + credentialGetters.OPEN_AI, + getOpenAiScores, + ), + [SignalType.OPEN_AI_SELF_HARM_INTENT_IMAGE_MODEL]: + new OpenAiSelfHarmIntentImageSignal( + credentialGetters.OPEN_AI, + getOpenAiScores, + ), + [SignalType.OPEN_AI_SELF_HARM_INTENT_TEXT_MODEL]: + new OpenAiSelfHarmIntentTextSignal( + credentialGetters.OPEN_AI, + getOpenAiScores, + ), [SignalType.OPEN_AI_SELF_HARM_TEXT_MODEL]: new OpenAiSelfHarmTextSignal( credentialGetters.OPEN_AI, getOpenAiScores, diff --git a/server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiGraphicViolenceImageSignal.ts b/server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiGraphicViolenceImageSignal.ts index 0aa1f5ff..168061ec 100644 --- a/server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiGraphicViolenceImageSignal.ts +++ b/server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiGraphicViolenceImageSignal.ts @@ -1,5 +1,5 @@ import { SignalType } from '../../../../types/SignalType.js'; -import { makeOpenAiImageModerationSignal } from './openAIModerationUtils.js'; +import { makeOpenAiImageModerationSignal } from './openAiModerationSignalFactory.js'; /** * OpenAI image-moderation signal scoring whether an image depicts death, diff --git a/server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiSelfHarmImageSignal.ts b/server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiSelfHarmImageSignal.ts index 14d3e7e5..b7cb56d2 100644 --- a/server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiSelfHarmImageSignal.ts +++ b/server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiSelfHarmImageSignal.ts @@ -1,5 +1,5 @@ import { SignalType } from '../../../../types/SignalType.js'; -import { makeOpenAiImageModerationSignal } from './openAIModerationUtils.js'; +import { makeOpenAiImageModerationSignal } from './openAiModerationSignalFactory.js'; /** * OpenAI image-moderation signal scoring whether an image promotes, diff --git a/server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiSelfHarmInstructionsImageSignal.ts b/server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiSelfHarmInstructionsImageSignal.ts new file mode 100644 index 00000000..5bc0cc9d --- /dev/null +++ b/server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiSelfHarmInstructionsImageSignal.ts @@ -0,0 +1,19 @@ +import { SignalType } from '../../../../types/SignalType.js'; +import { makeOpenAiImageModerationSignal } from './openAiModerationSignalFactory.js'; + +/** + * OpenAI image-moderation signal scoring whether an image encourages or + * provides instructions for self-harm. Routes through omni-moderation-latest's + * multimodal endpoint and returns the `self-harm/instructions` category score + * (0..1). + */ +const OpenAiSelfHarmInstructionsImageSignal = makeOpenAiImageModerationSignal({ + type: SignalType.OPEN_AI_SELF_HARM_INSTRUCTIONS_IMAGE_MODEL, + displayName: 'OpenAI Self-Harm Instructions Image score', + description: `OpenAI's model that detects content that encourages performing acts of self-harm, such as suicide, cutting, and eating disorders, or that gives instructions or advice on how to commit such acts. Scored against the image. + + This model produces a confidence score between 0 and 1, indicating the model's confidence that the image contains self-harm instructions.`, + modelName: 'self-harm/instructions', +}); + +export default OpenAiSelfHarmInstructionsImageSignal; diff --git a/server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiSelfHarmInstructionsTextSignal.ts b/server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiSelfHarmInstructionsTextSignal.ts new file mode 100644 index 00000000..0ff2fe19 --- /dev/null +++ b/server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiSelfHarmInstructionsTextSignal.ts @@ -0,0 +1,19 @@ +import { SignalType } from '../../../../types/SignalType.js'; +import { makeOpenAiTextModerationSignal } from './openAiModerationSignalFactory.js'; + +/** + * OpenAI text-moderation signal scoring whether text encourages or provides + * instructions for self-harm (suicide, cutting, eating disorders, etc.). + * Routes through omni-moderation-latest and returns the + * `self-harm/instructions` category score (0..1). + */ +const OpenAiSelfHarmInstructionsTextSignal = makeOpenAiTextModerationSignal({ + type: SignalType.OPEN_AI_SELF_HARM_INSTRUCTIONS_TEXT_MODEL, + displayName: 'OpenAI Self-Harm Instructions Text score', + description: `OpenAI's model that detects content that encourages performing acts of self-harm, such as suicide, cutting, and eating disorders, or that gives instructions or advice on how to commit such acts. + + This model produces a confidence score between 0 and 1, indicating the model's confidence that the content contains self-harm instructions.`, + modelName: 'self-harm/instructions', +}); + +export default OpenAiSelfHarmInstructionsTextSignal; diff --git a/server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiSelfHarmIntentImageSignal.ts b/server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiSelfHarmIntentImageSignal.ts new file mode 100644 index 00000000..3ec08f37 --- /dev/null +++ b/server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiSelfHarmIntentImageSignal.ts @@ -0,0 +1,19 @@ +import { SignalType } from '../../../../types/SignalType.js'; +import { makeOpenAiImageModerationSignal } from './openAiModerationSignalFactory.js'; + +/** + * OpenAI image-moderation signal scoring whether an image expresses the + * speaker's intent to engage in acts of self-harm. Routes through + * omni-moderation-latest's multimodal endpoint and returns the + * `self-harm/intent` category score (0..1). + */ +const OpenAiSelfHarmIntentImageSignal = makeOpenAiImageModerationSignal({ + type: SignalType.OPEN_AI_SELF_HARM_INTENT_IMAGE_MODEL, + displayName: 'OpenAI Self-Harm Intent Image score', + description: `OpenAI's model that detects self-harm intent, which is defined as content where the speaker expresses that they are engaging or intend to engage in acts of self-harm, such as suicide, cutting, and eating disorders. Scored against the image. + + This model produces a confidence score between 0 and 1, indicating the model's confidence that the image expresses self-harm intent.`, + modelName: 'self-harm/intent', +}); + +export default OpenAiSelfHarmIntentImageSignal; diff --git a/server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiSelfHarmIntentTextSignal.ts b/server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiSelfHarmIntentTextSignal.ts new file mode 100644 index 00000000..62acec33 --- /dev/null +++ b/server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiSelfHarmIntentTextSignal.ts @@ -0,0 +1,19 @@ +import { SignalType } from '../../../../types/SignalType.js'; +import { makeOpenAiTextModerationSignal } from './openAiModerationSignalFactory.js'; + +/** + * OpenAI text-moderation signal scoring whether text expresses the speaker's + * intent to engage in acts of self-harm (suicide, cutting, eating disorders, + * etc.). Routes through omni-moderation-latest and returns the + * `self-harm/intent` category score (0..1). + */ +const OpenAiSelfHarmIntentTextSignal = makeOpenAiTextModerationSignal({ + type: SignalType.OPEN_AI_SELF_HARM_INTENT_TEXT_MODEL, + displayName: 'OpenAI Self-Harm Intent Text score', + description: `OpenAI's model that detects self-harm intent, which is defined as content where the speaker expresses that they are engaging or intend to engage in acts of self-harm, such as suicide, cutting, and eating disorders. + + This model produces a confidence score between 0 and 1, indicating the model's confidence that the content expresses self-harm intent.`, + modelName: 'self-harm/intent', +}); + +export default OpenAiSelfHarmIntentTextSignal; diff --git a/server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiSexualImageSignal.ts b/server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiSexualImageSignal.ts index f2bdee74..22067d4e 100644 --- a/server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiSexualImageSignal.ts +++ b/server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiSexualImageSignal.ts @@ -1,5 +1,5 @@ import { SignalType } from '../../../../types/SignalType.js'; -import { makeOpenAiImageModerationSignal } from './openAIModerationUtils.js'; +import { makeOpenAiImageModerationSignal } from './openAiModerationSignalFactory.js'; /** * OpenAI image-moderation signal scoring whether an image is meant to arouse diff --git a/server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiViolenceImageSignal.ts b/server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiViolenceImageSignal.ts index f251fc39..1946d377 100644 --- a/server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiViolenceImageSignal.ts +++ b/server/services/signalsService/signals/third_party_signals/open_ai/moderation/OpenAiViolenceImageSignal.ts @@ -1,5 +1,5 @@ import { SignalType } from '../../../../types/SignalType.js'; -import { makeOpenAiImageModerationSignal } from './openAIModerationUtils.js'; +import { makeOpenAiImageModerationSignal } from './openAiModerationSignalFactory.js'; /** * OpenAI image-moderation signal scoring whether an image promotes, diff --git a/server/services/signalsService/signals/third_party_signals/open_ai/moderation/openAIModerationUtils.ts b/server/services/signalsService/signals/third_party_signals/open_ai/moderation/openAIModerationUtils.ts index 0ea1e8cb..d8b4d8a3 100644 --- a/server/services/signalsService/signals/third_party_signals/open_ai/moderation/openAIModerationUtils.ts +++ b/server/services/signalsService/signals/third_party_signals/open_ai/moderation/openAIModerationUtils.ts @@ -12,8 +12,7 @@ import { type CachedGetCredentials } from '../../../../../signalAuthService/sign import { Integration } from '../../../../types/Integration.js'; import { type RecommendedThresholds } from '../../../../types/RecommendedThresholds.js'; import { SignalPricingStructure } from '../../../../types/SignalPricingStructure.js'; -import { type SignalType } from '../../../../types/SignalType.js'; -import SignalBase, { +import { type SignalDisabledInfo, type SignalInput, } from '../../../SignalBase.js'; @@ -24,6 +23,8 @@ export type OpenAiModelName = | 'hate' | 'hate/threatening' | 'self-harm' + | 'self-harm/instructions' + | 'self-harm/intent' | 'sexual' | 'sexual/minors' | 'violence' @@ -38,7 +39,12 @@ export type OpenAiModelName = */ export type OpenAiImageModelName = Extract< OpenAiModelName, - 'self-harm' | 'sexual' | 'violence' | 'violence/graphic' + | 'self-harm' + | 'self-harm/instructions' + | 'self-harm/intent' + | 'sexual' + | 'violence' + | 'violence/graphic' >; const OPEN_AI_MODERATION_MODEL = 'omni-moderation-latest'; @@ -285,115 +291,3 @@ export async function getOpenAiModerationScores( throw e; } } - -/** - * Factory for OpenAI image-moderation signals. All four image signals - * (`violence`, `violence/graphic`, `self-harm`, `sexual`) share identical - * boilerplate: same integration, pricing, language coverage, eligible inputs, - * etc. — the only per-signal config is the SignalType id, display name, - * description, and the model category to read. - * - * Returns a `SignalBase` subclass that the IoC container instantiates with - * `(credentials, scores)` like any other signal class, preserving the - * existing registration pattern in `instantiateBuiltInSignals.ts`. - */ -export function makeOpenAiImageModerationSignal(config: { - type: SignalType; - displayName: string; - description: string; - modelName: OpenAiImageModelName; -}) { - return class OpenAiImageModerationSignal extends SignalBase< - ScalarTypes['IMAGE'], - { scalarType: ScalarTypes['NUMBER'] } - > { - constructor( - protected readonly getOpenAiCredentials: CachedGetCredentials<'OPEN_AI'>, - protected readonly getOpenAiScores: FetchOpenAiModerationScores, - ) { - super(); - } - - override get id() { - return { type: config.type }; - } - - override get displayName() { - return config.displayName; - } - - override get description() { - return config.description; - } - - override get docsUrl() { - return openAiModerationDocsUrl(); - } - - override get integration() { - return openAiModerationIntegration(); - } - - override get pricingStructure() { - return openAiModerationPricingStructure(); - } - - override get recommendedThresholds() { - return openAiModerationRecommendedThresholds(); - } - - override get supportedLanguages() { - return openAiModerationSupportedLanguages(); - } - - override get eligibleSubcategories() { - return openAiModerationEligibleSubcategories(); - } - - override get needsActionPenalties() { - return openAiModerationNeedsActionPenalties(); - } - - override get needsMatchingValues() { - return openAiModerationNeedsMatchingValues(); - } - - override async getDisabledInfo(orgId: string) { - return openAiModerationGetDisabledInfo(orgId, this.getOpenAiCredentials); - } - - override get eligibleInputs() { - return [ScalarTypes.IMAGE]; - } - - override get outputType() { - return { scalarType: ScalarTypes.NUMBER }; - } - - // Inherits the placeholder cost convention from the existing OpenAI text - // signals (see OpenAiViolenceTextSignal etc.). Cost units are unitless - // ordering hints used by the engine to prefer cheaper signals; the - // current ~20 baseline reflects that OpenAI moderation is a paid - // remote-API call (vs. local heuristics) — not a calibrated value. - override getCost() { - return 20; - } - - override get allowedInAutomatedRules() { - return true; - } - - /** - * Fetches the omni-moderation `${config.modelName}` score for the image - * and returns it as a number between 0 and 1. - */ - async run(input: SignalInput) { - return runOpenAiModerationImageImpl( - this.getOpenAiCredentials, - input, - this.getOpenAiScores, - config.modelName, - ); - } - }; -} diff --git a/server/services/signalsService/signals/third_party_signals/open_ai/moderation/openAiModerationSignalFactory.ts b/server/services/signalsService/signals/third_party_signals/open_ai/moderation/openAiModerationSignalFactory.ts new file mode 100644 index 00000000..b6c527fe --- /dev/null +++ b/server/services/signalsService/signals/third_party_signals/open_ai/moderation/openAiModerationSignalFactory.ts @@ -0,0 +1,191 @@ +/** + * Factories for OpenAI moderation signals. Collapses the boilerplate that + * was duplicated across every per-category signal class — id, displayName, + * description, integration, pricing, languages, cost, etc. The two public + * exports (`makeOpenAiImageModerationSignal`, + * `makeOpenAiTextModerationSignal`) are thin wrappers around the private + * `makeOpenAiModerationSignal` so the class body lives in one place. + * + * The IoC container instantiates the returned class with + * `(credentials, scores)` like any other signal, preserving the existing + * registration pattern in `instantiateBuiltInSignals.ts`. + */ +import { ScalarTypes } from '@roostorg/coop-types'; + +import { type CachedGetCredentials } from '../../../../../signalAuthService/signalAuthService.js'; +import { type SignalType } from '../../../../types/SignalType.js'; +import SignalBase, { + type SignalInput, + type SignalInputType, +} from '../../../SignalBase.js'; +import { + openAiModerationDocsUrl, + openAiModerationEligibleSubcategories, + openAiModerationGetDisabledInfo, + openAiModerationIntegration, + openAiModerationNeedsActionPenalties, + openAiModerationNeedsMatchingValues, + openAiModerationPricingStructure, + openAiModerationRecommendedThresholds, + openAiModerationSupportedLanguages, + runOpenAiModerationImageImpl, + runOpenAiModerationImpl, + type FetchOpenAiModerationScores, + type OpenAiImageModelName, + type OpenAiModelName, +} from './openAIModerationUtils.js'; + +type SignalRunResult = { + score: number; + outputType: { scalarType: ScalarTypes['NUMBER'] }; +}; + +type ModerationMode = { + /** ScalarType key returned by `eligibleInputs` (e.g. `ScalarTypes.IMAGE`). */ + inputScalar: InputScalar; + /** Routes to either `runOpenAiModerationImpl` or `runOpenAiModerationImageImpl`. */ + runImpl: ( + getOpenAiCredentials: CachedGetCredentials<'OPEN_AI'>, + input: SignalInput, + getOpenAiScores: FetchOpenAiModerationScores, + modelName: ModelName, + ) => Promise; +}; + +type SignalConfig = { + type: SignalType; + displayName: string; + description: string; + modelName: ModelName; +}; + +function makeOpenAiModerationSignal< + InputScalar extends SignalInputType, + ModelName, +>(config: SignalConfig & ModerationMode) { + return class OpenAiModerationSignal extends SignalBase< + InputScalar, + { scalarType: ScalarTypes['NUMBER'] } + > { + constructor( + protected readonly getOpenAiCredentials: CachedGetCredentials<'OPEN_AI'>, + protected readonly getOpenAiScores: FetchOpenAiModerationScores, + ) { + super(); + } + + override get id() { + return { type: config.type }; + } + + override get displayName() { + return config.displayName; + } + + override get description() { + return config.description; + } + + override get docsUrl() { + return openAiModerationDocsUrl(); + } + + override get integration() { + return openAiModerationIntegration(); + } + + override get pricingStructure() { + return openAiModerationPricingStructure(); + } + + override get recommendedThresholds() { + return openAiModerationRecommendedThresholds(); + } + + override get supportedLanguages() { + return openAiModerationSupportedLanguages(); + } + + override get eligibleSubcategories() { + return openAiModerationEligibleSubcategories(); + } + + override get needsActionPenalties() { + return openAiModerationNeedsActionPenalties(); + } + + override get needsMatchingValues() { + return openAiModerationNeedsMatchingValues(); + } + + override async getDisabledInfo(orgId: string) { + return openAiModerationGetDisabledInfo(orgId, this.getOpenAiCredentials); + } + + override get eligibleInputs() { + return [config.inputScalar]; + } + + override get outputType() { + return { scalarType: ScalarTypes.NUMBER }; + } + + // Matches the legacy OpenAI text-signal baseline (`getCost: 20`). Values + // here are unitless ordering hints used by the rule engine to prefer + // cheaper signals — they're not calibrated against latency or $ across + // the codebase. Re-calibrating is tracked separately; perpetuating the + // existing baseline keeps new signals consistent with their text peers. + override getCost() { + return 20; + } + + override get allowedInAutomatedRules() { + return true; + } + + /** + * Fetches the omni-moderation `${config.modelName}` score for the input + * and returns it as a number between 0 and 1. + */ + async run(input: SignalInput) { + return config.runImpl( + this.getOpenAiCredentials, + input, + this.getOpenAiScores, + config.modelName, + ); + } + }; +} + +/** + * Factory for image-input OpenAI moderation signals. The `modelName` + * parameter is constrained to {@link OpenAiImageModelName} so callers can't + * request a category OpenAI only scores against text. + */ +export function makeOpenAiImageModerationSignal( + config: SignalConfig, +) { + return makeOpenAiModerationSignal( + { + ...config, + inputScalar: ScalarTypes.IMAGE, + runImpl: runOpenAiModerationImageImpl, + }, + ); +} + +/** + * Factory for text-input OpenAI moderation signals. Accepts the full + * {@link OpenAiModelName} since omni-moderation scores every category on + * text. + */ +export function makeOpenAiTextModerationSignal( + config: SignalConfig, +) { + return makeOpenAiModerationSignal({ + ...config, + inputScalar: ScalarTypes.STRING, + runImpl: runOpenAiModerationImpl, + }); +} diff --git a/server/services/signalsService/types/SignalArgsByType.ts b/server/services/signalsService/types/SignalArgsByType.ts index 44c0b0d0..83ac4cec 100644 --- a/server/services/signalsService/types/SignalArgsByType.ts +++ b/server/services/signalsService/types/SignalArgsByType.ts @@ -29,6 +29,10 @@ export type SignalArgsByType = Satisfies< [SignalType.OPEN_AI_HATE_TEXT_MODEL]: undefined; [SignalType.OPEN_AI_HATE_THREATENING_TEXT_MODEL]: undefined; [SignalType.OPEN_AI_SELF_HARM_IMAGE_MODEL]: undefined; + [SignalType.OPEN_AI_SELF_HARM_INSTRUCTIONS_IMAGE_MODEL]: undefined; + [SignalType.OPEN_AI_SELF_HARM_INSTRUCTIONS_TEXT_MODEL]: undefined; + [SignalType.OPEN_AI_SELF_HARM_INTENT_IMAGE_MODEL]: undefined; + [SignalType.OPEN_AI_SELF_HARM_INTENT_TEXT_MODEL]: undefined; [SignalType.OPEN_AI_SELF_HARM_TEXT_MODEL]: undefined; [SignalType.OPEN_AI_SEXUAL_IMAGE_MODEL]: undefined; [SignalType.OPEN_AI_SEXUAL_MINORS_TEXT_MODEL]: undefined; @@ -67,6 +71,10 @@ export type RuntimeSignalArgsByType = Satisfies< [SignalType.OPEN_AI_HATE_TEXT_MODEL]: undefined; [SignalType.OPEN_AI_HATE_THREATENING_TEXT_MODEL]: undefined; [SignalType.OPEN_AI_SELF_HARM_IMAGE_MODEL]: undefined; + [SignalType.OPEN_AI_SELF_HARM_INSTRUCTIONS_IMAGE_MODEL]: undefined; + [SignalType.OPEN_AI_SELF_HARM_INSTRUCTIONS_TEXT_MODEL]: undefined; + [SignalType.OPEN_AI_SELF_HARM_INTENT_IMAGE_MODEL]: undefined; + [SignalType.OPEN_AI_SELF_HARM_INTENT_TEXT_MODEL]: undefined; [SignalType.OPEN_AI_SELF_HARM_TEXT_MODEL]: undefined; [SignalType.OPEN_AI_SEXUAL_IMAGE_MODEL]: undefined; [SignalType.OPEN_AI_SEXUAL_MINORS_TEXT_MODEL]: undefined; diff --git a/server/services/signalsService/types/SignalType.ts b/server/services/signalsService/types/SignalType.ts index 2c3c8074..f6570ecb 100644 --- a/server/services/signalsService/types/SignalType.ts +++ b/server/services/signalsService/types/SignalType.ts @@ -44,6 +44,10 @@ export const BuiltInThirdPartySignalType = makeEnumLike([ 'OPEN_AI_HATE_TEXT_MODEL', 'OPEN_AI_HATE_THREATENING_TEXT_MODEL', 'OPEN_AI_SELF_HARM_IMAGE_MODEL', + 'OPEN_AI_SELF_HARM_INSTRUCTIONS_IMAGE_MODEL', + 'OPEN_AI_SELF_HARM_INSTRUCTIONS_TEXT_MODEL', + 'OPEN_AI_SELF_HARM_INTENT_IMAGE_MODEL', + 'OPEN_AI_SELF_HARM_INTENT_TEXT_MODEL', 'OPEN_AI_SELF_HARM_TEXT_MODEL', 'OPEN_AI_SEXUAL_IMAGE_MODEL', 'OPEN_AI_SEXUAL_MINORS_TEXT_MODEL', @@ -98,6 +102,10 @@ export function integrationForSignalType(type: SignalType | string) { case 'OPEN_AI_HATE_TEXT_MODEL': case 'OPEN_AI_HATE_THREATENING_TEXT_MODEL': case 'OPEN_AI_SELF_HARM_IMAGE_MODEL': + case 'OPEN_AI_SELF_HARM_INSTRUCTIONS_IMAGE_MODEL': + case 'OPEN_AI_SELF_HARM_INSTRUCTIONS_TEXT_MODEL': + case 'OPEN_AI_SELF_HARM_INTENT_IMAGE_MODEL': + case 'OPEN_AI_SELF_HARM_INTENT_TEXT_MODEL': case 'OPEN_AI_SELF_HARM_TEXT_MODEL': case 'OPEN_AI_SEXUAL_IMAGE_MODEL': case 'OPEN_AI_SEXUAL_MINORS_TEXT_MODEL': From 6e4a158af00c27cdefb01f37c95f7e67f7b27c23 Mon Sep 17 00:00:00 2001 From: Juan Mrad Date: Wed, 5 Aug 2026 23:10:40 -0500 Subject: [PATCH 52/57] Disable max lines lint on tests files (#970) --- server/.eslintrc.cjs | 1 + 1 file changed, 1 insertion(+) diff --git a/server/.eslintrc.cjs b/server/.eslintrc.cjs index b51c42f5..1e723004 100644 --- a/server/.eslintrc.cjs +++ b/server/.eslintrc.cjs @@ -694,6 +694,7 @@ module.exports = { }, ], 'no-console': 'off', + 'max-lines': 'off', // Allow `typeof import('...')` annotations; needed for E2E tests. '@typescript-eslint/consistent-type-imports': [ 'error', From 1eb63ca05a5f9c308da0c6164269b89dd0315fcb Mon Sep 17 00:00:00 2001 From: Caleb McQuaid Date: Thu, 6 Aug 2026 00:52:32 -0400 Subject: [PATCH 53/57] log lock release failures --- .../modules/QueueOperations.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/server/services/manualReviewToolService/modules/QueueOperations.ts b/server/services/manualReviewToolService/modules/QueueOperations.ts index 761e1344..9092ef2c 100644 --- a/server/services/manualReviewToolService/modules/QueueOperations.ts +++ b/server/services/manualReviewToolService/modules/QueueOperations.ts @@ -1368,7 +1368,17 @@ export default class QueueOperations { queueId, jobId: held.data.id, lockToken, - }).catch(() => {}); + }).catch((error) => { + // Not rethrown: this runs in a `finally`, so throwing would replace + // whatever result or error the caller was about to receive. The + // failure is self-healing — the lock expires on its own after + // `lockDuration` — but it shouldn't be silent. + // eslint-disable-next-line no-console + console.error( + `Failed to release held-aside job lock (queue ${queueId}, job ${held.data.id}):`, + error, + ); + }); } } } From b2c9010ffd62b101f76ff68af283587b2c6e0ef4 Mon Sep 17 00:00:00 2001 From: Caleb McQuaid Date: Thu, 6 Aug 2026 01:22:57 -0400 Subject: [PATCH 54/57] test harness for reviewer-skip tests --- .../QueueOperations.reviewerSkips.test.ts | 72 ++++++++----------- 1 file changed, 30 insertions(+), 42 deletions(-) diff --git a/server/services/manualReviewToolService/modules/QueueOperations.reviewerSkips.test.ts b/server/services/manualReviewToolService/modules/QueueOperations.reviewerSkips.test.ts index fe5bcadb..a9816507 100644 --- a/server/services/manualReviewToolService/modules/QueueOperations.reviewerSkips.test.ts +++ b/server/services/manualReviewToolService/modules/QueueOperations.reviewerSkips.test.ts @@ -1,10 +1,9 @@ import { uid } from 'uid'; -import getBottle from '../../../iocContainer/index.js'; import createMrtQueue from '../../../test/fixtureHelpers/createMrtQueue.js'; import createOrg from '../../../test/fixtureHelpers/createOrg.js'; import createUser from '../../../test/fixtureHelpers/createUser.js'; -import { makeTestWithFixture } from '../../../test/utils.js'; +import { makeTransactionalTestWithFixture } from '../../../test/harness/transactionalTest.js'; import { instantiateOpaqueType } from '../../../utils/typescript-types.js'; import { makeSubmissionId, @@ -14,45 +13,34 @@ import { type ItemSubmissionWithTypeIdentifier } from '../../itemProcessingServi import { type ManualReviewJobPayload } from '../manualReviewToolService.js'; describe('QueueOperations per-reviewer skips', () => { - const testWithQueue = () => - makeTestWithFixture(async () => { - const container = (await getBottle()).container; - - const { org, cleanup: orgCleanup } = await createOrg( - { - KyselyPg: container.KyselyPg, - ModerationConfigService: container.ModerationConfigService, - ApiKeyService: container.ApiKeyService, - }, - uid(), - ); - - const { user, cleanup: userCleanup } = await createUser( - container.KyselyPg, - org.id, - ); - - const { queue, cleanup: queuesCleanup } = await createMrtQueue({ - orgId: org.id, - mrtService: container.ManualReviewToolService, - userId: user.id, - }); - - return { - org, - queue, - user, - mrtService: container.ManualReviewToolService, - cleanup: async () => { - await queuesCleanup(); - await userCleanup(); - await orgCleanup(); - await container.KyselyPg.destroy(); - await container.KyselyPgReadReplica.destroy(); - }, - }; + // Runs inside a transaction that rolls back, so the fixtures need no manual + // teardown. + const testWithQueue = makeTransactionalTestWithFixture(async ({ deps }) => { + const { org } = await createOrg( + { + KyselyPg: deps.KyselyPg, + ModerationConfigService: deps.ModerationConfigService, + ApiKeyService: deps.ApiKeyService, + }, + uid(), + ); + + const { user } = await createUser(deps.KyselyPg, org.id); + + const { queue } = await createMrtQueue({ + orgId: org.id, + mrtService: deps.ManualReviewToolService, + userId: user.id, }); + return { + org, + queue, + user, + mrtService: deps.ManualReviewToolService, + }; + }); + const makePayloadFor = (itemTypeId: string) => (itemId: string): ManualReviewJobPayload => ({ @@ -75,7 +63,7 @@ describe('QueueOperations per-reviewer skips', () => { enqueueSourceInfo: { kind: 'REPORT' }, }); - testWithQueue()( + testWithQueue( 'a skipped job is hidden from that reviewer but immediately available to others', async ({ org, queue, mrtService }) => { const queueOps = mrtService['queueOps']; @@ -119,7 +107,7 @@ describe('QueueOperations per-reviewer skips', () => { }, ); - testWithQueue()( + testWithQueue( 'a queue whose only jobs are skipped returns null instead of hanging', async ({ org, queue, mrtService }) => { const queueOps = mrtService['queueOps']; @@ -148,7 +136,7 @@ describe('QueueOperations per-reviewer skips', () => { }, ); - testWithQueue()( + testWithQueue( 'logSkip hides the job from the skipper and releases their lock in one call', async ({ org, queue, user, mrtService }) => { const queueOps = mrtService['queueOps']; From f02ab105339263b600d30512d5e5462120c76014 Mon Sep 17 00:00:00 2001 From: Caleb McQuaid Date: Thu, 6 Aug 2026 01:51:40 -0400 Subject: [PATCH 55/57] release the job lock inside recordReviewerSkip --- .../manualReviewToolService.ts | 12 ++---------- .../modules/QueueOperations.ts | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/server/services/manualReviewToolService/manualReviewToolService.ts b/server/services/manualReviewToolService/manualReviewToolService.ts index 2c4095a1..a4836514 100644 --- a/server/services/manualReviewToolService/manualReviewToolService.ts +++ b/server/services/manualReviewToolService/manualReviewToolService.ts @@ -1667,22 +1667,14 @@ export class ManualReviewToolService { userId: string; }) { await this.skipOps.logSkip(opts); - // Hide the job from THIS reviewer for the skip window; the dequeue path - // reads this back and steps past it. + // Hides the job from THIS reviewer for the skip window and releases their + // lock so it returns to the shared pool immediately for everyone else. await this.queueOps.recordReviewerSkip({ orgId: opts.orgId, queueId: opts.queueId, reviewerId: opts.userId, jobId: opts.jobId, }); - // Release the reviewer's lock (the lock token is the reviewer's userId) - // so the job returns to the shared pool immediately for everyone else. - await this.releaseJobLock({ - orgId: opts.orgId, - queueId: opts.queueId, - jobId: opts.jobId, - lockToken: opts.userId, - }); } async releaseJobLock(opts: { diff --git a/server/services/manualReviewToolService/modules/QueueOperations.ts b/server/services/manualReviewToolService/modules/QueueOperations.ts index 9092ef2c..6f1fce41 100644 --- a/server/services/manualReviewToolService/modules/QueueOperations.ts +++ b/server/services/manualReviewToolService/modules/QueueOperations.ts @@ -1389,6 +1389,15 @@ export default class QueueOperations { return `{${orgId}}:mrt-reviewer-skips:${queueId}:${reviewerId}`; } + /** + * Hides a job from one reviewer for the skip window and hands it straight + * back to everyone else. + * + * Releasing the lock is part of skipping, not a separate step callers have to + * remember — there is no case for recording a skip while still holding the + * job. `releaseJobLock` is a no-op when no lock is held, so this is safe even + * when the caller never took one. + */ async recordReviewerSkip(opts: { orgId: string; queueId: string; @@ -1401,6 +1410,14 @@ export default class QueueOperations { await this.redis.zadd(key, expiresAt, jobId); // Backstop: the whole set disappears once everything in it has expired. await this.redis.pexpire(key, QueueOperations.REVIEWER_SKIP_TTL_MS); + + // The lock token is the reviewer's own id. + await this.releaseJobLock({ + orgId, + queueId, + jobId: instantiateOpaqueType(jobId), + lockToken: reviewerId, + }); } async getActiveReviewerSkips(opts: { From db04c287857227e3657d2b3cd4869b47516736cd Mon Sep 17 00:00:00 2001 From: Caleb McQuaid Date: Tue, 11 Aug 2026 01:00:35 -0400 Subject: [PATCH 56/57] remove changelog --- CHANGELOG.md | 9 --------- .../manualReviewToolService.test.ts | 1 - .../manualReviewToolService/modules/JobRouting.test.ts | 1 - .../modules/ReporterInvalidation.test.ts | 1 - .../moderationConfigService.test.ts | 1 - 5 files changed, 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 24e7a82b..69f1c51b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,15 +2,6 @@ **Full Changelog**: https://github.com/roostorg/coop/compare/1.0.2...main -## Review Console - -- Added per-queue job sort modes for manual review: FIFO (default, unchanged), most-reported-first, and custom weighted — ordering is computed at enqueue via BullMQ job priority, so the locked dequeue path is unchanged (#718) -- Added org-configurable job priority weights (Settings → Job Priorities) for weighted queues; changing weights or a queue's sort mode re-sorts already-queued jobs in a background sweep (#892) -- Skip is now per-reviewer: a skipped job is hidden from that reviewer for 30 minutes while returning to the shared pool immediately; entering a drained or fully-skipped queue redirects to the queue list (#893) -- Fixed a "Job submission failed" error that prevented reviewers from clearing jobs whose item had an unparseable `Created At` value; the decision now records instead of failing (#913) -- Fixed a "Something Went Wrong" page when opening a job or queue whose `Created At` value was unparseable; the affected date now shows `Unknown` instead of blanking the view (#916) -- Fixed "Oldest Task Age" on the MRT queues dashboard showing the newest job's age instead of the oldest (#909) - # Coop 1.0.2 This release addresses reported security advisories, improves NCMEC CyberTipline reporting, and includes front-end quality-of-life improvements. diff --git a/server/services/manualReviewToolService/manualReviewToolService.test.ts b/server/services/manualReviewToolService/manualReviewToolService.test.ts index 9d0402ff..4987de8b 100644 --- a/server/services/manualReviewToolService/manualReviewToolService.test.ts +++ b/server/services/manualReviewToolService/manualReviewToolService.test.ts @@ -1,4 +1,3 @@ - import { uid } from 'uid'; import { v1 as uuidv1 } from 'uuid'; diff --git a/server/services/manualReviewToolService/modules/JobRouting.test.ts b/server/services/manualReviewToolService/modules/JobRouting.test.ts index fc426673..eca6eb3d 100644 --- a/server/services/manualReviewToolService/modules/JobRouting.test.ts +++ b/server/services/manualReviewToolService/modules/JobRouting.test.ts @@ -1,4 +1,3 @@ - import { ScalarTypes } from '@roostorg/coop-types'; import { uid } from 'uid'; diff --git a/server/services/manualReviewToolService/modules/ReporterInvalidation.test.ts b/server/services/manualReviewToolService/modules/ReporterInvalidation.test.ts index fa0d53b7..e7a745b7 100644 --- a/server/services/manualReviewToolService/modules/ReporterInvalidation.test.ts +++ b/server/services/manualReviewToolService/modules/ReporterInvalidation.test.ts @@ -1,4 +1,3 @@ - import { uid } from 'uid'; import { v1 as uuidv1 } from 'uuid'; diff --git a/server/services/moderationConfigService/moderationConfigService.test.ts b/server/services/moderationConfigService/moderationConfigService.test.ts index 3d96c8e0..4447c8e0 100644 --- a/server/services/moderationConfigService/moderationConfigService.test.ts +++ b/server/services/moderationConfigService/moderationConfigService.test.ts @@ -1,4 +1,3 @@ - import { faker } from '@faker-js/faker'; import { Kysely } from 'kysely'; import { type UnionToIntersection } from 'type-fest'; From 61101734b7606a3e6a235681b79a55934e516200 Mon Sep 17 00:00:00 2001 From: Caleb McQuaid Date: Tue, 11 Aug 2026 01:20:06 -0400 Subject: [PATCH 57/57] skipToNextJob, swallowing error fixes --- .../ManualReviewJobReview.tsx | 44 +++++++++++++---- .../modules/QueueOperations.ts | 48 +++++++++++++------ 2 files changed, 67 insertions(+), 25 deletions(-) diff --git a/client/src/webpages/dashboard/mrt/manual_review_job/ManualReviewJobReview.tsx b/client/src/webpages/dashboard/mrt/manual_review_job/ManualReviewJobReview.tsx index 887f901a..7f2ca4e5 100644 --- a/client/src/webpages/dashboard/mrt/manual_review_job/ManualReviewJobReview.tsx +++ b/client/src/webpages/dashboard/mrt/manual_review_job/ManualReviewJobReview.tsx @@ -777,26 +777,50 @@ function ManualReviewJobReviewImpl(props: { }, [jobId, queueId, refetchJobInfo, advanceToNextJobAfterInvalidation]); const skipToNextJob = async () => { + // This is wired straight to a button's `onClick`, so nothing downstream + // catches a rejection: any error escaping here is an unhandled promise + // rejection and the reviewer gets no feedback at all. Both the skip and + // the follow-up dequeue therefore handle rejection explicitly. + const showSkipFailed = () => + setModalInfo({ + visible: true, + modalBody: 'Failed to skip this job. Please try again.', + footer: [{ title: 'Ok', type: 'primary', onClick: hideModal }], + }); + // Skipping is one server-side operation: it logs the skip, hides the job // from this reviewer for the skip window, and releases the lock so the // job returns to the shared pool for everyone else. if (queueId && job?.id && lockToken) { - const result = await logSkip(); - if (result.data?.logSkip !== true) { - // Nothing was released or hidden; stay on the current job so the - // reviewer can retry (or decide) instead of advancing past it. - setModalInfo({ - visible: true, - modalBody: 'Failed to skip this job. Please try again.', - footer: [{ title: 'Ok', type: 'primary', onClick: hideModal }], - }); + let skipped: boolean; + try { + const result = await logSkip(); + skipped = result.data?.logSkip === true; + } catch { + // Network/GraphQL failure. Same reviewer-facing outcome as a falsy + // response: nothing was released or hidden. + skipped = false; + } + if (!skipped) { + // Stay on the current job so the reviewer can retry (or decide) + // instead of advancing past it. + showSkipFailed(); return; } } // Reset state and try to get the next job resetState(); - const result = await getNextJob(); + let result; + try { + result = await getNextJob(); + } catch { + // The skip already succeeded, so the job is gone from this reviewer's + // view; only the advance failed. Surface it rather than leaving the + // reviewer on a job they no longer hold. + showSkipFailed(); + return; + } // If there's no next job, redirect to the queues page if (result.data?.dequeueManualReviewJob == null) { diff --git a/server/services/manualReviewToolService/modules/QueueOperations.ts b/server/services/manualReviewToolService/modules/QueueOperations.ts index 8539fa7b..2aecef6b 100644 --- a/server/services/manualReviewToolService/modules/QueueOperations.ts +++ b/server/services/manualReviewToolService/modules/QueueOperations.ts @@ -1349,7 +1349,12 @@ export default class QueueOperations { queueId, lockToken, jobId: job.data.id, - }).catch(() => {}); + }).catch((error: unknown) => { + // Non-fatal: the scan should still hand this reviewer a job. But it + // must not be silent — see the note on the same call in + // `dequeueNextJobWithLock`. + this.tracer.logActiveSpanFailedIfAny(error); + }); // then continue while loop } else { // this is the most likely case, where there is a job @@ -1418,7 +1423,19 @@ export default class QueueOperations { queueId, lockToken, jobId: convertedJob.data.id, - }).catch(() => {}); + }).catch((error: unknown) => { + // Non-fatal, but not silent either. The common cause is another + // reviewer having re-locked the job, in which case they will + // retire it and doing nothing is correct. The case that matters is + // a Redis failure: the decided job then stays `active` until its + // lock expires, after which the stalled checker returns it to + // `wait` — forever. `maxStalledCount` does not bound this, because + // BullMQ only applies the deferred stall failure inside + // `processJob`, which never runs on these workers (`autorun: + // false`). So a stuck decided job recirculates indefinitely and + // this report is the only signal it happened. + this.tracer.logActiveSpanFailedIfAny(error); + }); // then continue while loop } else { // this is the most likely case, where there is a job @@ -1430,21 +1447,17 @@ export default class QueueOperations { // Release the held-aside jobs so other reviewers can pick them up // immediately. This reviewer stays excluded via the skip set. for (const held of heldAside) { + // No `.catch` here on purpose: `releaseJobLock` never rejects — it + // reports its own failures to the active span and returns. Chaining a + // handler here would be dead code. Releasing is best-effort anyway; a + // failure leaves the job locked until `lockDuration` expires, and + // throwing from a `finally` would replace whatever result or error the + // caller was about to receive. await this.releaseJobLock({ orgId, queueId, jobId: held.data.id, lockToken, - }).catch((error) => { - // Not rethrown: this runs in a `finally`, so throwing would replace - // whatever result or error the caller was about to receive. The - // failure is self-healing — the lock expires on its own after - // `lockDuration` — but it shouldn't be silent. - // eslint-disable-next-line no-console - console.error( - `Failed to release held-aside job lock (queue ${queueId}, job ${held.data.id}):`, - error, - ); }); } } @@ -1774,9 +1787,14 @@ export default class QueueOperations { // The token parameter ensures only the holder of the lock can release it await job.moveToDelayed(Date.now(), lockToken); } catch (error: unknown) { - // If the lock has already expired or the job is in a different state, - // we can safely ignore the error as the job is already released - // or will be handled by the stalled job checker + // Non-fatal: if the lock has already expired or the job moved state, the + // job is effectively released already (or the stalled checker will get + // it), and callers — including the `releaseJobLock` mutation and the + // held-aside loop in `dequeueNextJobWithLock` — treat releasing as + // best-effort cleanup that must not fail the operation around it. + // Reported rather than swallowed: a persistent failure here keeps a job + // locked for the full `lockDuration`, which is invisible otherwise. + this.tracer.logActiveSpanFailedIfAny(error); } }