From b1be39956ebb68b89615e4e4ac5c0d85c6a8083c Mon Sep 17 00:00:00 2001 From: Nicolas DUBIEN Date: Thu, 16 Apr 2026 00:19:16 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=9D=20Simplify=20examples=20(#6867)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description Simplify examples by removing dead code, consolidating tests, and improving naming: - Remove deprecated arbitrary implementations (OldWay, MemoBased) - Delete buggy/alternative source files (Counter, SynchronizedCounter, AutocompleteFieldSimple, AutocompleteFieldMostRecentQuery, buggyKnight) - Remove entire supertest example and its dependencies (express, supertest) - Strip bug-toggle props from AutocompleteField, UserProfilePage, DebouncedAutocomplete, and dependencyTree - Drop redundant tests: isSearchTree "(2)", counter "two concurrent", userProfile simple variant, knight hardcoded CodinGame cases - Rename property descriptions for clarity across all spec files - Remove per-file configureGlobal CodeSandbox boilerplate ## Checklist — _Don't delete this checklist and make sure you do the following before opening the PR_ - [x] I have a full understanding of every line in this PR — whether the code was hand-written, AI-generated, copied from external sources or produced by any other tool - [x] I flagged the impact of my change (minor / patch / major) either by running `pnpm run bump` or by following the instructions from the changeset bot - [x] I kept this PR focused on a single concern and did not bundle unrelated changes - [x] I followed the [gitmoji](https://gitmoji.dev/) specification for the name of the PR, including the package scope (e.g. `🐛(vitest) Something...`) when the change targets a package other than `fast-check` - [x] I added relevant tests and they would have failed without my PR (when applicable) --------- Co-authored-by: Claude Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> --- examples/001-simple/decompPrime/main.spec.ts | 6 +- examples/001-simple/fibonacci/main.spec.ts | 14 +- examples/001-simple/indexOf/main.spec.ts | 4 +- .../arbitraries/BinarySearchTreeArbitrary.ts | 27 -- .../arbitraries/BinaryTreeArbitrary.ts | 38 --- .../002-recursive/isSearchTree/main.spec.ts | 8 +- examples/003-misc/knight/main.spec.ts | 43 +-- examples/003-misc/knight/src/knight.ts | 38 --- .../004-stateMachine/musicPlayer/main.spec.ts | 1 - examples/005-race/autocomplete/main.spec.tsx | 30 +- .../autocomplete/src/AutocompleteField.tsx | 24 +- .../src/AutocompleteFieldMostRecentQuery.tsx | 62 ---- .../src/AutocompleteFieldSimple.tsx | 52 --- examples/005-race/counter/main.spec.ts | 14 +- examples/005-race/counter/src/Counter.ts | 9 - .../counter/src/SynchronizedCounter.ts | 18 - .../src/DebouncedAutocomplete.tsx | 7 +- examples/005-race/dependencyTree/main.spec.ts | 9 +- .../dependencyTree/src/dependencyTree.ts | 7 +- examples/005-race/supertest/app.spec.ts | 127 ------- examples/005-race/supertest/src/app.ts | 25 -- examples/005-race/supertest/src/appBug.ts | 18 - examples/005-race/supertest/src/db.ts | 9 - examples/005-race/userProfile/main.spec.tsx | 23 +- .../userProfile/src/UserProfilePage.tsx | 7 +- examples/README.md | 2 +- examples/package.json | 5 +- examples/tsconfig.json | 3 +- pnpm-lock.yaml | 321 +----------------- 29 files changed, 49 insertions(+), 902 deletions(-) delete mode 100644 examples/005-race/autocomplete/src/AutocompleteFieldMostRecentQuery.tsx delete mode 100644 examples/005-race/autocomplete/src/AutocompleteFieldSimple.tsx delete mode 100644 examples/005-race/counter/src/Counter.ts delete mode 100644 examples/005-race/counter/src/SynchronizedCounter.ts delete mode 100644 examples/005-race/supertest/app.spec.ts delete mode 100644 examples/005-race/supertest/src/app.ts delete mode 100644 examples/005-race/supertest/src/appBug.ts delete mode 100644 examples/005-race/supertest/src/db.ts diff --git a/examples/001-simple/decompPrime/main.spec.ts b/examples/001-simple/decompPrime/main.spec.ts index 842cc1c9..f073e665 100644 --- a/examples/001-simple/decompPrime/main.spec.ts +++ b/examples/001-simple/decompPrime/main.spec.ts @@ -6,7 +6,7 @@ import { decompPrime } from './src/decompPrime.js'; const MAX_INPUT = 65536; describe('decompPrime', () => { - it('should produce an array such that the product equals the input', () => { + it('should produce factors whose product equals the input', () => { fc.assert( fc.property(fc.nat(MAX_INPUT), (n) => { const factors = decompPrime(n); @@ -16,7 +16,7 @@ describe('decompPrime', () => { ); }); - it('should be able to decompose a product of two numbers', () => { + it('should produce at least 2 factors for any composite number', () => { fc.assert( fc.property(fc.integer({ min: 2, max: MAX_INPUT }), fc.integer({ min: 2, max: MAX_INPUT }), (a, b) => { const n = a * b; @@ -26,7 +26,7 @@ describe('decompPrime', () => { ); }); - it('should compute the same factors as to the concatenation of the one of a and b for a times b', () => { + it('should satisfy factors(a*b) = factors(a) ++ factors(b)', () => { fc.assert( fc.property(fc.integer({ min: 2, max: MAX_INPUT }), fc.integer({ min: 2, max: MAX_INPUT }), (a, b) => { const factorsA = decompPrime(a); diff --git a/examples/001-simple/fibonacci/main.spec.ts b/examples/001-simple/fibonacci/main.spec.ts index 8012e71c..47185906 100644 --- a/examples/001-simple/fibonacci/main.spec.ts +++ b/examples/001-simple/fibonacci/main.spec.ts @@ -7,7 +7,7 @@ import { fibo } from './src/fibonacci.js'; const MaxN = 1000; describe('fibonacci', () => { - it('should be equal to the sum of fibo(n-1) and fibo(n-2)', () => { + it('should satisfy the recurrence relation: fibo(n) = fibo(n-1) + fibo(n-2)', () => { fc.assert( fc.property(fc.integer({ min: 2, max: MaxN }), (n) => { expect(fibo(n)).toBe(fibo(n - 1) + fibo(n - 2)); @@ -18,7 +18,7 @@ describe('fibonacci', () => { // The following properties are listed on the Wikipedia page: // https://fr.wikipedia.org/wiki/Suite_de_Fibonacci#Divisibilit%C3%A9_des_nombres_de_Fibonacci - it('should fulfill fibo(p)*fibo(q+1)+fibo(p-1)*fibo(q) = fibo(p+q)', () => { + it('should satisfy the addition formula', () => { fc.assert( fc.property(fc.integer({ min: 1, max: MaxN }), fc.integer({ min: 0, max: MaxN }), (p, q) => { expect(fibo(p + q)).toBe(fibo(p) * fibo(q + 1) + fibo(p - 1) * fibo(q)); @@ -26,7 +26,7 @@ describe('fibonacci', () => { ); }); - it('should fulfill fibo(2p-1) = fibo²(p-1)+fibo²(p)', () => { + it('should satisfy the double-angle formula (special case of addition)', () => { // Special case of the property above fc.assert( fc.property(fc.integer({ min: 1, max: MaxN }), (p) => { @@ -35,7 +35,7 @@ describe('fibonacci', () => { ); }); - it('should fulfill Catalan identity', () => { + it('should satisfy the Catalan identity', () => { fc.assert( fc.property(fc.integer({ min: 0, max: MaxN }), fc.integer({ min: 0, max: MaxN }), (a, b) => { const [p, q] = a < b ? [b, a] : [a, b]; @@ -45,7 +45,7 @@ describe('fibonacci', () => { ); }); - it('should fulfill Cassini identity', () => { + it('should satisfy the Cassini identity', () => { fc.assert( fc.property(fc.integer({ min: 1, max: MaxN }), (p) => { const sign = p % 2 === 0 ? 1n : -1n; // (-1)^p @@ -54,7 +54,7 @@ describe('fibonacci', () => { ); }); - it('should fibo(nk) divisible by fibo(n)', () => { + it('should satisfy divisibility: fibo(n*k) is divisible by fibo(n)', () => { fc.assert( fc.property(fc.integer({ min: 1, max: MaxN }), fc.integer({ min: 0, max: 100 }), (n, k) => { expect(fibo(n * k) % fibo(n)).toBe(0n); @@ -62,7 +62,7 @@ describe('fibonacci', () => { ); }); - it('should fulfill gcd(fibo(a), fibo(b)) = fibo(gcd(a,b))', () => { + it('should satisfy the GCD identity: gcd(fibo(a), fibo(b)) = fibo(gcd(a,b))', () => { fc.assert( fc.property(fc.integer({ min: 1, max: MaxN }), fc.integer({ min: 1, max: MaxN }), (a, b) => { const gcd = (a: T, b: T, zero: T): T => { diff --git a/examples/001-simple/indexOf/main.spec.ts b/examples/001-simple/indexOf/main.spec.ts index b0e8d373..533efb30 100644 --- a/examples/001-simple/indexOf/main.spec.ts +++ b/examples/001-simple/indexOf/main.spec.ts @@ -3,7 +3,7 @@ import fc from 'fast-check'; import { indexOf } from './src/indexOf.js'; describe('indexOf', () => { - it('should confirm b is a substring of a + b + c', () => { + it('should always find b within the concatenation a + b + c', () => { fc.assert( fc.property(fc.string(), fc.string(), fc.string(), (a, b, c) => { return indexOf(a + b + c, b) !== -1; @@ -11,7 +11,7 @@ describe('indexOf', () => { ); }); - it('should return the starting position of the pattern within text if any', () => { + it('should return an index where the pattern actually occurs', () => { fc.assert( fc.property(fc.string(), fc.string(), fc.string(), (a, b, c) => { const text = a + b + c; diff --git a/examples/002-recursive/isSearchTree/arbitraries/BinarySearchTreeArbitrary.ts b/examples/002-recursive/isSearchTree/arbitraries/BinarySearchTreeArbitrary.ts index 50028b9e..bde99a26 100644 --- a/examples/002-recursive/isSearchTree/arbitraries/BinarySearchTreeArbitrary.ts +++ b/examples/002-recursive/isSearchTree/arbitraries/BinarySearchTreeArbitrary.ts @@ -27,30 +27,3 @@ export const binarySearchTreeWithMaxDepth = (maxDepth: number): fc.Arbitrary> => { - const valueArbitrary = fc.integer({ min: minValue, max: maxValue }); - if (maxDepth <= 0) { - return fc.record({ - value: valueArbitrary, - left: fc.constant(null), - right: fc.constant(null), - }); - } - return valueArbitrary.chain((rootValue) => { - const leftArb = binarySearchTreeWithMaxDepthOldWay(maxDepth - 1, minValue, rootValue); - const rightArb = - rootValue < maxValue - ? binarySearchTreeWithMaxDepthOldWay(maxDepth - 1, rootValue + 1, maxValue) - : fc.constant(null); - return fc.record({ - value: fc.constant(rootValue), - left: fc.oneof(fc.constant(null), leftArb), - right: fc.oneof(fc.constant(null), rightArb), - }); - }); -}; diff --git a/examples/002-recursive/isSearchTree/arbitraries/BinaryTreeArbitrary.ts b/examples/002-recursive/isSearchTree/arbitraries/BinaryTreeArbitrary.ts index 65bcd183..64d921a6 100644 --- a/examples/002-recursive/isSearchTree/arbitraries/BinaryTreeArbitrary.ts +++ b/examples/002-recursive/isSearchTree/arbitraries/BinaryTreeArbitrary.ts @@ -26,41 +26,3 @@ export const binaryTreeWithoutMaxDepth = (): fc.Arbitrary> => { })); return tree as fc.Arbitrary>; }; - -// Alternative solutions -// Prefer one of the implementation above. - -export const binaryTreeWithMaxDepthMemoBased = (maxDepth: number): fc.Arbitrary> => { - // Prefer letrec implementation: arbitrary is less expensive to build - - const leaf: fc.Arbitrary> = fc.record({ - value: fc.integer(), - left: fc.constant(null), - right: fc.constant(null), - }); - - const node: fc.Memo> = fc.memo((n) => { - if (n <= 1) return leaf; - return fc.record({ value: fc.integer(), left: tree(n - 1), right: tree(n - 1) }); - }); - - const tree: fc.Memo> = fc.memo((n) => fc.oneof(leaf, node(n))); - return tree(maxDepth); -}; - -export function binaryTreeWithMaxDepthOldWay(maxDepth: number): fc.Arbitrary> { - const valueArbitrary = fc.integer(); - if (maxDepth <= 0) { - return fc.record({ - value: valueArbitrary, - left: fc.constant(null), - right: fc.constant(null), - }); - } - const subTree = fc.oneof(fc.constant(null), binaryTreeWithMaxDepthOldWay(maxDepth - 1)); - return fc.record({ - value: valueArbitrary, - left: subTree, - right: subTree, - }); -} diff --git a/examples/002-recursive/isSearchTree/main.spec.ts b/examples/002-recursive/isSearchTree/main.spec.ts index dcb9f025..f31ce427 100644 --- a/examples/002-recursive/isSearchTree/main.spec.ts +++ b/examples/002-recursive/isSearchTree/main.spec.ts @@ -6,7 +6,7 @@ import { isSearchTree, Tree } from './src/isSearchTree.js'; import { binaryTreeWithMaxDepth, binaryTreeWithoutMaxDepth } from './arbitraries/BinaryTreeArbitrary.js'; describe('isSearchTree', () => { - it('should always mark binary search trees as search trees', () => { + it('should accept valid binary search trees', () => { fc.assert( fc.property(binarySearchTreeWithMaxDepth(3), (tree) => { return isSearchTree(tree); @@ -14,7 +14,7 @@ describe('isSearchTree', () => { ); }); - it('should detect invalid search trees whenever tree traversal produces unordered arrays', () => { + it('should reject trees with unordered in-order traversal', () => { fc.assert( fc.property(binaryTreeWithMaxDepth(3), (tree) => { fc.pre(!isSorted(traversal(tree, (t) => t.value))); @@ -23,7 +23,7 @@ describe('isSearchTree', () => { ); }); - it('should detect invalid search trees whenever tree traversal produces unordered arrays (2)', () => { + it('should reject trees with unordered in-order traversal (depthSize)', () => { fc.assert( fc.property(binaryTreeWithoutMaxDepth(), (tree) => { fc.pre(!isSorted(traversal(tree, (t) => t.value))); @@ -32,7 +32,7 @@ describe('isSearchTree', () => { ); }); - it('should detect invalid search trees whenever one node in the tree has an invalid direct child', () => { + it('should reject trees where a child violates the BST ordering', () => { fc.assert( fc.property(binaryTreeWithMaxDepth(3), (tree) => { fc.pre( diff --git a/examples/003-misc/knight/main.spec.ts b/examples/003-misc/knight/main.spec.ts index 8e065eb6..b6f56734 100644 --- a/examples/003-misc/knight/main.spec.ts +++ b/examples/003-misc/knight/main.spec.ts @@ -1,12 +1,8 @@ -import { describe, it, expect } from 'vitest'; +import { describe, it } from 'vitest'; import fc from 'fast-check'; import { SpaceArbitrary } from './arbitraries/SpaceArbitrary.js'; -import { SpaceBuilder } from './src/space.js'; import { knight } from './src/knight.js'; -// const solver = buggyKnight; // with bugs -const solver = knight; - describe('knight', () => { it('should always reach its target', () => { fc.assert( @@ -17,41 +13,4 @@ describe('knight', () => { }), ); }); - - // The following set of tests is directly taken from CodinGame - it('should succeed on: A lot of jumps', () => { - const space = new SpaceBuilder().withDimension(4, 8).withSolution(3, 7).withCurrent(2, 3).build(); - solver(space, 40); - expect(space.solved()).toBe(true); - }); - it('should succeed on: Less jumps', () => { - const space = new SpaceBuilder().withDimension(25, 33).withSolution(2, 29).withCurrent(24, 2).build(); - solver(space, 49); - expect(space.solved()).toBe(true); - }); - it('should succeed on: Lesser jumps', () => { - const space = new SpaceBuilder().withDimension(40, 60).withSolution(38, 38).withCurrent(6, 6).build(); - solver(space, 32); - expect(space.solved()).toBe(true); - }); - it('should succeed on: Tower', () => { - const space = new SpaceBuilder().withDimension(1, 80).withSolution(0, 36).withCurrent(0, 1).build(); - solver(space, 6); - expect(space.solved()).toBe(true); - }); - it('should succeed on: Correct cutting', () => { - const space = new SpaceBuilder().withDimension(50, 50).withSolution(22, 22).withCurrent(0, 0).build(); - solver(space, 6); - expect(space.solved()).toBe(true); - }); - it('should succeed on: Evasive', () => { - const space = new SpaceBuilder().withDimension(100, 100).withSolution(0, 1).withCurrent(5, 98).build(); - solver(space, 7); - expect(space.solved()).toBe(true); - }); - it('should succeed on: Not there', () => { - const space = new SpaceBuilder().withDimension(9999, 9999).withSolution(9754, 2531).withCurrent(54, 77).build(); - solver(space, 14); - expect(space.solved()).toBe(true); - }); }); diff --git a/examples/003-misc/knight/src/knight.ts b/examples/003-misc/knight/src/knight.ts index 8e619bee..c6b8b2a8 100644 --- a/examples/003-misc/knight/src/knight.ts +++ b/examples/003-misc/knight/src/knight.ts @@ -1,43 +1,5 @@ import { Space } from './space.js'; -// This implementation is supposed to solve the CodinGame: -// https://www.codingame.com/training/medium/shadows-of-the-knight-episode-1 -// -// Among the two implementations below, one is bugged the other not - -export function buggyKnight(space: Space, rounds: number) { - let x_min = 0; - let x_max = space.dim_x; - let y_min = 0; - let y_max = space.dim_y; - - for (let n = 0; n !== rounds && !space.solved(); ++n) { - if (x_min >= x_max || y_min >= y_max) { - return; - } - - let { x: x0, y: y0 } = space.readPosition(); - const hint = space.readHint(); - - if (hint[0] == 'U') { - y_max = y0 - 1; - y0 = (y_max + y_min) / 2; - } else if (hint[0] == 'D') { - y_min = y0 + 1; - y0 = (y_max + y_min) / 2; - } - - if (hint.slice(-1) == 'L') { - x_max = x0 - 1; - x0 = (x_max + x_min) / 2; - } else if (hint.slice(-1) == 'R') { - x_min = x0 + 1; - x0 = (x_max + x_min) / 2; - } - space.move(Math.floor(x0), Math.floor(y0)); - } -} - export function knight(space: Space, rounds: number) { let x_min = 0; let x_max = space.dim_x; diff --git a/examples/004-stateMachine/musicPlayer/main.spec.ts b/examples/004-stateMachine/musicPlayer/main.spec.ts index dbb54c53..a3e6c983 100644 --- a/examples/004-stateMachine/musicPlayer/main.spec.ts +++ b/examples/004-stateMachine/musicPlayer/main.spec.ts @@ -8,7 +8,6 @@ describe('MusicPlayer', () => { it('should detect potential issues with the MusicPlayer', () => fc.assert( fc.property(fc.uniqueArray(TrackNameArb, { minLength: 1 }), MusicPlayerCommands, (initialTracks, commands) => { - // const real = new MusicPlayerImplem(initialTracks, true); // with bugs const real = new MusicPlayerImplem(initialTracks); const model = new MusicPlayerModel(); model.numTracks = initialTracks.length; diff --git a/examples/005-race/autocomplete/main.spec.tsx b/examples/005-race/autocomplete/main.spec.tsx index fba17cd1..fbe1951a 100644 --- a/examples/005-race/autocomplete/main.spec.tsx +++ b/examples/005-race/autocomplete/main.spec.tsx @@ -6,39 +6,21 @@ import fc from 'fast-check'; import * as React from 'react'; import AutocompleteField from './src/AutocompleteField.js'; -//import AutocompleteField from './src/AutocompleteFieldMostRecentQuery.js'; -//import AutocompleteField from './src/AutocompleteFieldSimple.js'; import { render, cleanup, fireEvent, act, getNodeText, screen } from '@testing-library/react'; -import { search } from './src/Api.js'; - -// If you want to test the behaviour of fast-check in case of a bug: -const bugs = { - // enableBugBetterResults: true, - // enableBugUnfilteredResults: true, - // enableBugUnrelatedResults: true, - // enableBugDoNotDiscardOldQueries: true -}; - -if (!fc.readConfigureGlobal()) { - // Global config of Jest has been ignored, we will have a timeout after 5000ms - // (CodeSandbox falls in this category) - fc.configureGlobal({ interruptAfterTimeLimit: 4000 }); -} - describe('AutocompleteField', () => { - it('should suggest results matching the value of the autocomplete field', async () => { + it('should only show suggestions that match the current input', async () => { await fc.assert( fc .asyncProperty(AllResultsArbitrary, QueriesArbitrary, fc.scheduler({ act }), async (allResults, queries, s) => { // Arrange - const searchImplem: typeof search = s.scheduleFunction(function search(query, maxResults) { + const searchImplem = s.scheduleFunction(function search(query: string, maxResults: number) { return Promise.resolve(allResults.filter((r) => r.includes(query)).slice(0, maxResults)); }); // Act - render(); + render(); const input = screen.getByRole('textbox') as HTMLElement; s.scheduleSequence(buildAutocompleteEvents(input, queries)); @@ -62,18 +44,18 @@ describe('AutocompleteField', () => { ); }); - it('should display more and more sugestions as results come', async () => { + it('should show increasingly more suggestions as queries resolve', async () => { await fc.assert( fc .asyncProperty(AllResultsArbitrary, QueriesArbitrary, fc.scheduler({ act }), async (allResults, queries, s) => { // Arrange const query = queries[queries.length - 1]; - const searchImplem: typeof search = s.scheduleFunction(function search(query, maxResults) { + const searchImplem = s.scheduleFunction(function search(query: string, maxResults: number) { return Promise.resolve(allResults.filter((r) => r.includes(query)).slice(0, maxResults)); }); // Act - render(); + render(); const input = screen.getByRole('textbox') as HTMLElement; for (const event of buildAutocompleteEvents(input, queries)) { await event.builder(); diff --git a/examples/005-race/autocomplete/src/AutocompleteField.tsx b/examples/005-race/autocomplete/src/AutocompleteField.tsx index e0301479..c0b660bf 100644 --- a/examples/005-race/autocomplete/src/AutocompleteField.tsx +++ b/examples/005-race/autocomplete/src/AutocompleteField.tsx @@ -1,13 +1,6 @@ import React from 'react'; -// Injected as a props because CodeSandbox fails to provide jest.mock -// So it makes such import difficult to test -//// import { search } from './Api.js'; - type Props = { - enableBugUnrelatedResults?: boolean; - enableBugBetterResults?: boolean; - enableBugUnfilteredResults?: boolean; search: (query: string, maxResults: number) => Promise; }; @@ -21,21 +14,13 @@ export default function AutocompleteField(props: Props) { const runQuery = async () => { const results = await props.search(query, 10); - if (!lastQueryRef.current.startsWith(query) && !props.enableBugUnrelatedResults) { - // FIXED BUG: - // We show results for queries that are unrelated to the latest started query - // eg.: AZ resolves while we look for QS, we show its results even if totally unrelated + if (!lastQueryRef.current.startsWith(query)) { return; } if ( lastQueryRef.current.startsWith(lastSuccessfulQueryRef.current) && - lastSuccessfulQueryRef.current.length > query.length && - !props.enableBugBetterResults + lastSuccessfulQueryRef.current.length > query.length ) { - // FIXED BUG: - // We might update results while we already received results - // for a query less strict than the last this one - // eg.: We receice AZ while we already have results for AZE return; } @@ -59,10 +44,7 @@ export default function AutocompleteField(props: Props) { />
    {searchResults - // FIXED BUG: We don't filter the results we receive - // As we want to display results as soon as possible, even if our searchResults - // are related to a past query we want to use them to provide the user with some hints - .filter((r) => (props.enableBugUnfilteredResults ? true : r.startsWith(query))) + .filter((r) => r.startsWith(query)) .map((r) => (
  • {r}
  • ))} diff --git a/examples/005-race/autocomplete/src/AutocompleteFieldMostRecentQuery.tsx b/examples/005-race/autocomplete/src/AutocompleteFieldMostRecentQuery.tsx deleted file mode 100644 index e165f27c..00000000 --- a/examples/005-race/autocomplete/src/AutocompleteFieldMostRecentQuery.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import React from 'react'; - -// Injected as a props because CodeSandbox fails to provide jest.mock -// So it makes such import difficult to test -//// import { search } from './Api.js'; - -type Props = { - enableBugUnrelatedResults?: boolean; - enableBugUnfilteredResults?: boolean; - search: (query: string, maxResults: number) => Promise; -}; - -export default function AutocompleteField(props: Props) { - const lastQueryRef = React.useRef(''); - const lastQueryIdRef = React.useRef(0); - const lastSuccessfulQueryIdRef = React.useRef(lastQueryIdRef.current); - const [query, setQuery] = React.useState(lastQueryRef.current); - const [searchResults, setSearchResults] = React.useState([] as string[]); - - React.useEffect(() => { - const queryId = ++lastQueryIdRef.current; - const runQuery = async () => { - const results = await props.search(query, 10); - if (lastSuccessfulQueryIdRef.current > queryId) { - return; // A more recent query already succeeded - } - if (!lastQueryRef.current.startsWith(query) && !props.enableBugUnrelatedResults) { - // FIXED BUG: - // We show results for queries that are unrelated to the latest started query - // eg.: AZ resolves while we look for QS, we show its results even if totally unrelated - return; // Current query does not start with the query that just resolved - } - lastSuccessfulQueryIdRef.current = queryId; - setSearchResults(results); - }; - runQuery(); - }, [query, props]); - - return ( -
    - { - const value = (evt.target as any).value; - lastQueryRef.current = value; - setQuery(value); - }} - /> -
      - {searchResults - // FIXED BUG: We don't filter the results we receive - // As we want to display results as soon as possible, even if our searchResults - // are related to a past query we want to use them to provide the user with some hints - .filter((r) => (props.enableBugUnfilteredResults ? true : r.startsWith(query))) - .map((r) => ( -
    • {r}
    • - ))} -
    -
    - ); -} diff --git a/examples/005-race/autocomplete/src/AutocompleteFieldSimple.tsx b/examples/005-race/autocomplete/src/AutocompleteFieldSimple.tsx deleted file mode 100644 index c308efdc..00000000 --- a/examples/005-race/autocomplete/src/AutocompleteFieldSimple.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import React from 'react'; - -// Injected as a props because CodeSandbox fails to provide jest.mock -// So it makes such import difficult to test -//// import { search } from './Api.js'; - -type Props = { - enableBugDoNotDiscardOldQueries?: boolean; - enableBugUnfilteredResults?: boolean; - search: (query: string, maxResults: number) => Promise; -}; - -export default function AutocompleteField(props: Props) { - const [query, setQuery] = React.useState(''); - const [searchResults, setSearchResults] = React.useState([] as string[]); - - React.useEffect(() => { - let canceled = false; - const runQuery = async () => { - const results = await props.search(query, 10); - if (canceled && !props.enableBugDoNotDiscardOldQueries) return; - setSearchResults(results); - }; - runQuery(); - return () => { - canceled = true; - }; - }, [query, props]); - - return ( -
    - { - const value = (evt.target as any).value; - setQuery(value); - }} - /> -
      - {searchResults - // FIXED BUG: We don't filter the results we receive - // As we want to display results as soon as possible, even if our searchResults - // are related to a past query we want to use them to provide the user with some hints - .filter((r) => (props.enableBugUnfilteredResults ? true : r.startsWith(query))) - .map((r) => ( -
    • {r}
    • - ))} -
    -
    - ); -} diff --git a/examples/005-race/counter/main.spec.ts b/examples/005-race/counter/main.spec.ts index fc48d64a..de16b1db 100644 --- a/examples/005-race/counter/main.spec.ts +++ b/examples/005-race/counter/main.spec.ts @@ -2,17 +2,9 @@ import { describe, it, expect } from 'vitest'; import fc from 'fast-check'; import { CasCounter as Counter } from './src/CasCounter.js'; -//import { Counter } from './src/Counter.js'; -// import { SynchronizedCounter as Counter } from './src/SynchronizedCounter.js'; - -if (!fc.readConfigureGlobal()) { - // Global config of Jest has been ignored, we will have a timeout after 5000ms - // (CodeSandbox falls in this category) - fc.configureGlobal({ interruptAfterTimeLimit: 4000 }); -} describe('Counter', () => { - it('should handle two concurrent calls to "inc"', async () => { + it('should handle two concurrent calls to inc', async () => { await fc.assert( fc.asyncProperty(fc.scheduler(), async (s) => { // Arrange @@ -40,7 +32,7 @@ describe('Counter', () => { ); }); - it('should handle concurrent calls to "inc"', async () => { + it('should correctly count N concurrent increments', async () => { await fc.assert( fc.asyncProperty(fc.scheduler(), fc.nat(64), async (s, numCalls) => { // Arrange @@ -69,7 +61,7 @@ describe('Counter', () => { ); }); - it('should handle concurrent calls to "inc" on multiple "Counter"', async () => { + it('should correctly count concurrent increments across multiple counters', async () => { await fc.assert( fc.asyncProperty(fc.scheduler(), fc.array(fc.nat(64)), async (s, numCallsByCounter) => { // Arrange diff --git a/examples/005-race/counter/src/Counter.ts b/examples/005-race/counter/src/Counter.ts deleted file mode 100644 index 4b94cc05..00000000 --- a/examples/005-race/counter/src/Counter.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { DbConnection } from './DbConnection.js'; - -export class Counter { - constructor(private readonly db: DbConnection) {} - async inc(): Promise { - const count = await this.db.read(); - await this.db.write(count + 1); - } -} diff --git a/examples/005-race/counter/src/SynchronizedCounter.ts b/examples/005-race/counter/src/SynchronizedCounter.ts deleted file mode 100644 index d98094db..00000000 --- a/examples/005-race/counter/src/SynchronizedCounter.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { DbConnection } from './DbConnection.js'; - -export class SynchronizedCounter { - // Hacky trick to avoid using compare-and-swap - // Does not work when two counters are instantiated separately - private lock: Promise = Promise.resolve(); - constructor(private readonly db: DbConnection) {} - async inc(): Promise { - await this.synchronized(async () => { - const count = await this.db.read(); - await this.db.write(count + 1); - }); - } - private async synchronized(next: () => Promise): Promise { - this.lock = this.lock.then(next); - return this.lock; - } -} diff --git a/examples/005-race/debounced-autocomplete/src/DebouncedAutocomplete.tsx b/examples/005-race/debounced-autocomplete/src/DebouncedAutocomplete.tsx index 110daea3..58bead64 100644 --- a/examples/005-race/debounced-autocomplete/src/DebouncedAutocomplete.tsx +++ b/examples/005-race/debounced-autocomplete/src/DebouncedAutocomplete.tsx @@ -1,12 +1,11 @@ import React, { useEffect, useState } from 'react'; type Props = { - suggestionsFor: (query: string) => Promise; // Unable to mock imports in CodeSandbox - bug?: boolean; + suggestionsFor: (query: string) => Promise; }; export default function DebouncedAutocomplete(props: Props) { - const { bug, suggestionsFor } = props; + const { suggestionsFor } = props; const [query, setQuery] = useState(''); const [suggestions, setSuggestions] = useState([] as string[]); @@ -19,7 +18,7 @@ export default function DebouncedAutocomplete(props: Props) { const timer = setTimeout( () => suggestionsFor(query).then((suggestions) => { - if (!canceled || bug) { + if (!canceled) { setSuggestions(suggestions); } }), diff --git a/examples/005-race/dependencyTree/main.spec.ts b/examples/005-race/dependencyTree/main.spec.ts index b51b75c1..b904b1a6 100644 --- a/examples/005-race/dependencyTree/main.spec.ts +++ b/examples/005-race/dependencyTree/main.spec.ts @@ -3,12 +3,6 @@ import fc from 'fast-check'; import { dependencyTree, PackageDefinition } from './src/dependencyTree.js'; -if (!fc.readConfigureGlobal()) { - // Global config of Jest has been ignored, we will have a timeout after 5000ms - // (CodeSandbox falls in this category) - fc.configureGlobal({ interruptAfterTimeLimit: 4000 }); -} - describe('dependencyTree', () => { it('should be able to compute a dependency tree for any package of the registry', async () => { await fc.assert( @@ -20,8 +14,7 @@ describe('dependencyTree', () => { }); // Act - dependencyTree(selectedPackage, fetch); // without bugs - // dependencyTree(selectedPackage, fetch, true); // or with bugs + dependencyTree(selectedPackage, fetch); // Assert let numQueries = 0; diff --git a/examples/005-race/dependencyTree/src/dependencyTree.ts b/examples/005-race/dependencyTree/src/dependencyTree.ts index 13fa1d7d..e09b7013 100644 --- a/examples/005-race/dependencyTree/src/dependencyTree.ts +++ b/examples/005-race/dependencyTree/src/dependencyTree.ts @@ -1,16 +1,13 @@ export const dependencyTree = async ( initialPackageName: string, fetch: (packageName: string) => Promise, - withBug: boolean = false, ) => { const cache: AllPackagesDefinition = {}; const cachePending = new Set(); const feedCache = async (packageName: string) => { - if (!withBug) { - if (cachePending.has(packageName)) return; - cachePending.add(packageName); - } + if (cachePending.has(packageName)) return; + cachePending.add(packageName); if (cache[packageName]) return; const packageDef = await fetch(packageName); // cache miss diff --git a/examples/005-race/supertest/app.spec.ts b/examples/005-race/supertest/app.spec.ts deleted file mode 100644 index ed8a265e..00000000 --- a/examples/005-race/supertest/app.spec.ts +++ /dev/null @@ -1,127 +0,0 @@ -import { describe, it, expect, vi, beforeEach } from 'vitest'; -import type { MockedFunction } from 'vitest'; -import fc from 'fast-check'; -import request from 'supertest'; -import { app, dropDeactivatedInternal } from './src/app.js'; -// import { app, dropDeactivatedInternal } from'./src/appBug'; -import * as DbMock from './src/db.js'; - -vi.mock('./src/db'); - -if (!fc.readConfigureGlobal()) { - // Global config of Jest has been ignored, we will have a timeout after 5000ms - // (CodeSandbox falls in this category) - fc.configureGlobal({ interruptAfterTimeLimit: 4000 }); -} - -const beforeEachHook = () => { - vi.resetAllMocks(); -}; -beforeEach(beforeEachHook); -fc.configureGlobal({ ...fc.readConfigureGlobal(), beforeEach: beforeEachHook }); - -describe('app', () => { - it('should be able to call multiple /drop-deactivated at the same time', async () => { - await fc.assert( - fc.asyncProperty( - fc.uniqueArray( - fc.record({ - id: fc.uuid({ version: 4 }), - name: fc.string(), - deactivated: fc.boolean(), - }), - { selector: (u) => u.id }, - ), - fc.integer({ min: 1, max: 5 }), - fc.scheduler({ - // In the case of supertest, as a user you MUST define a custom `act` function - // if you want to run multiple queries at the same time. The following one adds - // some extra timers so that supertest can push promises in the queue in time. - // Second test is the very same test but outside of supertest. - act: async (f) => { - await new Promise((r) => setTimeout(r, 0)); - await f(); - }, - }), - async (allUsers, num, s) => { - // Arrange - let knownUsers = allUsers; - const getAllUsers = DbMock.getAllUsers as MockedFunction; - const removeUsers = DbMock.removeUsers as MockedFunction; - getAllUsers.mockImplementation( - s.scheduleFunction(async function getAllUsers() { - return knownUsers; - }), - ); - removeUsers.mockImplementation( - s.scheduleFunction(async function removeUsers(ids) { - const sizeBefore = knownUsers.length; - knownUsers = knownUsers.filter((u) => !ids.includes(u.id)); - return sizeBefore - knownUsers.length; - }), - ); - - // Act - const r = request(app); - const queries = []; - for (let index = 0; index !== num; ++index) { - queries.push(r.get('/drop-deactivated?id=' + index).send()); - } - const out = await s.waitFor(Promise.all(queries)); - - // Assert - for (const outQuery of out) { - expect(outQuery.body.status).toBe('success'); - } - }, - ), - ); - }); - - it('should be able to call multiple /drop-deactivated at the same time (no supertest)', async () => { - await fc.assert( - fc.asyncProperty( - fc.uniqueArray( - fc.record({ - id: fc.uuid({ version: 4 }), - name: fc.string(), - deactivated: fc.boolean(), - }), - { selector: (u) => u.id }, - ), - fc.integer({ min: 1, max: 5 }), - fc.scheduler(), - async (allUsers, num, s) => { - // Arrange - let knownUsers = allUsers; - const getAllUsers = DbMock.getAllUsers as MockedFunction; - const removeUsers = DbMock.removeUsers as MockedFunction; - getAllUsers.mockImplementation( - s.scheduleFunction(async function getAllUsers() { - return knownUsers; - }), - ); - removeUsers.mockImplementation( - s.scheduleFunction(async function removeUsers(ids) { - const sizeBefore = knownUsers.length; - knownUsers = knownUsers.filter((u) => !ids.includes(u.id)); - return sizeBefore - knownUsers.length; - }), - ); - - // Act - const queries = []; - for (let index = 0; index !== num; ++index) { - queries.push(dropDeactivatedInternal()); - } - const out = await s.waitFor(Promise.all(queries)); - - // Assert - for (const outQuery of out) { - expect(outQuery.status).toBe('success'); - } - }, - ), - ); - }); -}); diff --git a/examples/005-race/supertest/src/app.ts b/examples/005-race/supertest/src/app.ts deleted file mode 100644 index ae34f313..00000000 --- a/examples/005-race/supertest/src/app.ts +++ /dev/null @@ -1,25 +0,0 @@ -import express from 'express'; -import { getAllUsers, removeUsers } from './db.js'; - -const app: express.Application = express(); - -let lock: Promise = Promise.resolve(); - -async function dropDeactivatedInternal(): Promise<{ status: string }> { - async function synchronized() { - const allUsers = await getAllUsers(); - const usersToBeDeleted = allUsers.filter((u) => u.deactivated).map((u) => u.id); - const numDeleted = await removeUsers(usersToBeDeleted); - return { status: numDeleted === usersToBeDeleted.length ? 'success' : 'error' }; - } - const newLock = lock.then(synchronized, synchronized); - lock = newLock; - return newLock; -} - -app.get('/drop-deactivated', async function (req, res) { - const out = await dropDeactivatedInternal(); - res.status(200).json(out); -}); - -export { dropDeactivatedInternal, app }; diff --git a/examples/005-race/supertest/src/appBug.ts b/examples/005-race/supertest/src/appBug.ts deleted file mode 100644 index 4c5c5c47..00000000 --- a/examples/005-race/supertest/src/appBug.ts +++ /dev/null @@ -1,18 +0,0 @@ -import express from 'express'; -import { getAllUsers, removeUsers } from './db.js'; - -const app: express.Application = express(); - -async function dropDeactivatedInternal(): Promise<{ status: string }> { - const allUsers = await getAllUsers(); - const usersToBeDeleted = allUsers.filter((u) => u.deactivated).map((u) => u.id); - const numDeleted = await removeUsers(usersToBeDeleted); - return { status: numDeleted === usersToBeDeleted.length ? 'success' : 'error' }; -} - -app.get('/drop-deactivated', async function (req, res) { - const out = await dropDeactivatedInternal(); - res.status(200).json(out); -}); - -export { dropDeactivatedInternal, app }; diff --git a/examples/005-race/supertest/src/db.ts b/examples/005-race/supertest/src/db.ts deleted file mode 100644 index f0551668..00000000 --- a/examples/005-race/supertest/src/db.ts +++ /dev/null @@ -1,9 +0,0 @@ -export type User = { id: string; name: string; deactivated: boolean }; - -export async function getAllUsers(): Promise { - return []; -} - -export async function removeUsers(_ids: string[]): Promise { - return 0; -} diff --git a/examples/005-race/userProfile/main.spec.tsx b/examples/005-race/userProfile/main.spec.tsx index 685db7ae..52e5e6b2 100644 --- a/examples/005-race/userProfile/main.spec.tsx +++ b/examples/005-race/userProfile/main.spec.tsx @@ -9,15 +9,6 @@ import UserProfilePage from './src/UserProfilePage.js'; import { render, cleanup, act, screen } from '@testing-library/react'; -// If you want to test the behaviour of fast-check in case of a bug: -const bugId = undefined; // = 1; // to enable bug - -if (!fc.readConfigureGlobal()) { - // Global config of Jest has been ignored, we will have a timeout after 5000ms - // (CodeSandbox falls in this category) - fc.configureGlobal({ interruptAfterTimeLimit: 4000 }); -} - describe('UserProfilePage', () => { it('should not display data related to another user', async () => { await fc.assert( @@ -29,12 +20,10 @@ describe('UserProfilePage', () => { }); // Act - const { rerender } = render( - , - ); + const { rerender } = render(); s.scheduleSequence([ async () => { - rerender(); + rerender(); }, ]); await s.waitAll(); @@ -50,7 +39,7 @@ describe('UserProfilePage', () => { ); }); - it('should not display data related to another user (complex)', async () => { + it('should never display stale user data after userId changes', async () => { await fc.assert( fc .asyncProperty(fc.array(fc.uuid(), { minLength: 1 }), fc.scheduler(), async (loadedUserIds, s) => { @@ -61,15 +50,13 @@ describe('UserProfilePage', () => { // Act let currentUid = loadedUserIds[0]; - const { rerender } = render( - , - ); + const { rerender } = render(); s.scheduleSequence( loadedUserIds.slice(1).map((uid) => ({ label: `Update user id to ${uid}`, builder: async () => { currentUid = uid; - rerender(); + rerender(); }, })), ); diff --git a/examples/005-race/userProfile/src/UserProfilePage.tsx b/examples/005-race/userProfile/src/UserProfilePage.tsx index c278d3ab..1767e07f 100644 --- a/examples/005-race/userProfile/src/UserProfilePage.tsx +++ b/examples/005-race/userProfile/src/UserProfilePage.tsx @@ -4,9 +4,6 @@ type UserProfile = { id: string; name: string }; type Props = { userId: string; - bug?: 1; - // Injected as a props because CodeSandbox fails to provide jest.mock - // Otherwise we might have direclty imported it and mock the import getUserProfile: (userId: string) => Promise; }; @@ -18,13 +15,13 @@ export default function UserPageProfile(props: Props) { const fetchUser = async () => { setUserData(null); // reset on fetch const data = await props.getUserProfile(props.userId); - if (!canceled || props.bug !== undefined) setUserData(data); + if (!canceled) setUserData(data); }; fetchUser(); return () => { canceled = true; }; - }, [props.getUserProfile, props.userId, props.bug]); + }, [props.getUserProfile, props.userId]); if (userData === null) { return
    Loading...
    ; diff --git a/examples/README.md b/examples/README.md index 42060104..2cd56e03 100644 --- a/examples/README.md +++ b/examples/README.md @@ -44,7 +44,7 @@ Property based testing applied to state machines or user interfaces: Property based testing used to detect race conditions in various kind of JavaScript snippets: - `AutocompleteField` - An autocomplete field written in React providing suggestions as soon as possible -- `Counter` - Increment a counter stored in a DB - non atomic and atomic versions +- `Counter` - Increment a counter stored in a DB using compare-and-swap - `DebouncedAutocomplete` - An autocomplete field written in React providing suggestions in a debounced way (uses timers) - `dependencyTree` - Fetch recursively dependencies for a npm package - `TodoList` - Simple todolist React app diff --git a/examples/package.json b/examples/package.json index b7e669cc..a905028d 100644 --- a/examples/package.json +++ b/examples/package.json @@ -11,18 +11,15 @@ "@testing-library/dom": "^10.4.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", - "@types/express": "^5.0.6", "@types/lodash": "^4.17.24", + "@types/node": "^24.12.2", "@types/react": "^19.2.14", - "@types/supertest": "^7.2.0", - "express": "^5.2.1", "fast-check": "workspace:*", "happy-dom": "^20.8.9", "lodash": "^4.18.1", "pure-rand": "^8.4.0", "react": "^19.2.5", "react-dom": "^19.2.5", - "supertest": "^7.2.2", "typescript": "~6.0.2", "vitest": "^4.1.0" }, diff --git a/examples/tsconfig.json b/examples/tsconfig.json index 1205e679..9388a0d4 100644 --- a/examples/tsconfig.json +++ b/examples/tsconfig.json @@ -3,6 +3,7 @@ "compilerOptions": { "isolatedDeclarations": false, "jsx": "react", - "lib": ["dom", "dom.iterable", "esnext"] + "lib": ["dom", "dom.iterable", "esnext"], + "types": ["node"] } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 932c5ad5..a4f4b215 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -62,21 +62,15 @@ importers: '@testing-library/user-event': specifier: ^14.6.1 version: 14.6.1(@testing-library/dom@10.4.1) - '@types/express': - specifier: ^5.0.6 - version: 5.0.6 '@types/lodash': specifier: ^4.17.24 version: 4.17.24 + '@types/node': + specifier: ^24.12.2 + version: 24.12.2 '@types/react': specifier: ^19.2.14 version: 19.2.14 - '@types/supertest': - specifier: ^7.2.0 - version: 7.2.0 - express: - specifier: ^5.2.1 - version: 5.2.1 fast-check: specifier: workspace:* version: link:../packages/fast-check @@ -95,9 +89,6 @@ importers: react-dom: specifier: ^19.2.5 version: 19.2.5(react@19.2.5) - supertest: - specifier: ^7.2.2 - version: 7.2.2 typescript: specifier: ~6.0.2 version: 6.0.2 @@ -2252,10 +2243,6 @@ packages: resolution: {integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==} engines: {node: '>= 16'} - '@noble/hashes@1.8.0': - resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} - engines: {node: ^14.21.3 || >=16} - '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -2540,9 +2527,6 @@ packages: cpu: [x64] os: [win32] - '@paralleldrive/cuid2@2.3.1': - resolution: {integrity: sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==} - '@peculiar/asn1-cms@2.6.1': resolution: {integrity: sha512-vdG4fBF6Lkirkcl53q6eOdn3XYKt+kJTG59edgRZORlg/3atWWEReRCx5rYE1ZzTTX6vLK5zDMjHh7vbrcXGtw==} @@ -3275,9 +3259,6 @@ packages: '@types/connect@3.4.38': resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} - '@types/cookiejar@2.1.5': - resolution: {integrity: sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==} - '@types/d3-array@3.2.2': resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==} @@ -3458,9 +3439,6 @@ packages: '@types/mdx@2.0.13': resolution: {integrity: sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==} - '@types/methods@1.1.4': - resolution: {integrity: sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==} - '@types/mime@1.3.5': resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} @@ -3557,12 +3535,6 @@ packages: '@types/stack-utils@2.0.3': resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} - '@types/superagent@8.1.9': - resolution: {integrity: sha512-pTVjI73witn+9ILmoJdajHGW2jkSaOzhiFYF1Rd3EQ94kymLqB9PjD9ISg7WaALC7+dCHT0FGe9T2LktLq/3GQ==} - - '@types/supertest@7.2.0': - resolution: {integrity: sha512-uh2Lv57xvggst6lCqNdFAmDSvoMG7M/HDtX4iUCquxQ5EGPtaPM5PL5Hmi7LCvOG8db7YaCPNJEeoI8s/WzIQw==} - '@types/trusted-types@2.0.7': resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} @@ -3799,10 +3771,6 @@ packages: resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} engines: {node: '>= 0.6'} - accepts@2.0.0: - resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} - engines: {node: '>= 0.6'} - acorn-import-attributes@1.9.5: resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} peerDependencies: @@ -3976,9 +3944,6 @@ packages: resolution: {integrity: sha512-tLkvA81vQG/XqE2mjDkGQHoOINtMHtysSnemrmoGe6PydDPMRbVugqyk4A6V/WDWEfm3l+0d8anA9r8cv/5Jaw==} engines: {node: '>=12'} - asap@2.0.6: - resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} - asn1js@3.0.7: resolution: {integrity: sha512-uLvq6KJu04qoQM6gvBfKFjlh6Gl0vOKQuR5cJMDHQkmwfMOQeN3F3SHCv9SNYSL+CRoHvOGFfllDlVz03GQjvQ==} engines: {node: '>=12.0.0'} @@ -4150,10 +4115,6 @@ packages: resolution: {integrity: sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} - body-parser@2.2.2: - resolution: {integrity: sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==} - engines: {node: '>=18'} - bonjour-service@1.3.0: resolution: {integrity: sha512-3YuAUiSkWykd+2Azjgyxei8OWf8thdn8AITIog2M4UICzoqfjlqr64WIjEXZllf/W6vK1goqleSR6brGomxQqA==} @@ -4472,9 +4433,6 @@ packages: resolution: {integrity: sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==} engines: {node: '>=4.0.0'} - component-emitter@1.3.1: - resolution: {integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==} - compressible@2.0.18: resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==} engines: {node: '>= 0.6'} @@ -4516,10 +4474,6 @@ packages: resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} engines: {node: '>= 0.6'} - content-disposition@1.0.1: - resolution: {integrity: sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==} - engines: {node: '>=18'} - content-type@1.0.5: resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} engines: {node: '>= 0.6'} @@ -4534,17 +4488,10 @@ packages: cookie-signature@1.0.7: resolution: {integrity: sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==} - cookie-signature@1.2.2: - resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} - engines: {node: '>=6.6.0'} - cookie@0.7.2: resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} engines: {node: '>= 0.6'} - cookiejar@2.1.4: - resolution: {integrity: sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==} - copy-text-to-clipboard@3.2.2: resolution: {integrity: sha512-T6SqyLd1iLuqPA90J5N4cTalrtovCySh58iiZDGJ6FGznbclKh4UI+FGacQSgFzwKG77W7XT5gwbVEbd9cIH1A==} engines: {node: '>=12'} @@ -5021,9 +4968,6 @@ packages: devlop@1.1.0: resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} - dezalgo@1.0.4: - resolution: {integrity: sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==} - didyoumean@1.2.2: resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} @@ -5344,10 +5288,6 @@ packages: resolution: {integrity: sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==} engines: {node: '>= 0.10.0'} - express@5.2.1: - resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==} - engines: {node: '>= 18'} - extend-shallow@2.0.1: resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==} engines: {node: '>=0.10.0'} @@ -5375,9 +5315,6 @@ packages: fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} - fast-safe-stringify@2.1.1: - resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} - fast-uri@3.1.0: resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} @@ -5443,10 +5380,6 @@ packages: resolution: {integrity: sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==} engines: {node: '>= 0.8'} - finalhandler@2.1.1: - resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==} - engines: {node: '>= 18.0.0'} - find-cache-dir@4.0.0: resolution: {integrity: sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg==} engines: {node: '>=14.16'} @@ -5496,10 +5429,6 @@ packages: resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==} engines: {node: '>=0.4.x'} - formidable@3.5.4: - resolution: {integrity: sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==} - engines: {node: '>=14.0.0'} - forwarded@0.2.0: resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} engines: {node: '>= 0.6'} @@ -5511,10 +5440,6 @@ packages: resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} engines: {node: '>= 0.6'} - fresh@2.0.0: - resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} - engines: {node: '>= 0.8'} - fs-extra@11.3.4: resolution: {integrity: sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA==} engines: {node: '>=14.14'} @@ -6832,10 +6757,6 @@ packages: resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} engines: {node: '>= 0.6'} - media-typer@1.1.0: - resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} - engines: {node: '>= 0.8'} - memfs@4.57.1: resolution: {integrity: sha512-WvzrWPwMQT+PtbX2Et64R4qXKK0fj/8pO85MrUCzymX3twwCiJCdvntW3HdhG1teLJcHDDLIKx5+c3HckWYZtQ==} peerDependencies: @@ -6848,10 +6769,6 @@ packages: merge-descriptors@1.0.3: resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==} - merge-descriptors@2.0.0: - resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} - engines: {node: '>=18'} - merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} @@ -7022,11 +6939,6 @@ packages: engines: {node: '>=4'} hasBin: true - mime@2.6.0: - resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==} - engines: {node: '>=4.0.0'} - hasBin: true - mime@3.0.0: resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==} engines: {node: '>=10.0.0'} @@ -7515,9 +7427,6 @@ packages: path-to-regexp@3.3.0: resolution: {integrity: sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==} - path-to-regexp@8.4.0: - resolution: {integrity: sha512-PuseHIvAnz3bjrM2rGJtSgo1zjgxapTLZ7x2pjhzWwlp4SJQgK3f3iZIQwkpEnBaKz6seKBADpM4B4ySkuYypg==} - path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} @@ -8099,10 +8008,6 @@ packages: resolution: {integrity: sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==} engines: {node: '>=0.6'} - qs@6.15.0: - resolution: {integrity: sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==} - engines: {node: '>=0.6'} - quansync@0.2.11: resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} @@ -8140,10 +8045,6 @@ packages: resolution: {integrity: sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==} engines: {node: '>= 0.8'} - raw-body@3.0.2: - resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==} - engines: {node: '>= 0.10'} - rc@1.2.8: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} hasBin: true @@ -8408,10 +8309,6 @@ packages: roughjs@4.6.6: resolution: {integrity: sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==} - router@2.2.0: - resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} - engines: {node: '>= 18'} - rtlcss@4.3.0: resolution: {integrity: sha512-FI+pHEn7Wc4NqKXMXFM+VAYKEj/mRIcW4h24YVwVtyjI+EqGrLc2Hx/Ny0lrZ21cBWU2goLy36eqMcNj3AQJig==} engines: {node: '>=12.0.0'} @@ -8517,10 +8414,6 @@ packages: resolution: {integrity: sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==} engines: {node: '>= 0.8.0'} - send@1.2.1: - resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} - engines: {node: '>= 18'} - serialize-error@7.0.1: resolution: {integrity: sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==} engines: {node: '>=10'} @@ -8539,10 +8432,6 @@ packages: resolution: {integrity: sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==} engines: {node: '>= 0.8.0'} - serve-static@2.2.1: - resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==} - engines: {node: '>= 18'} - set-blocking@2.0.0: resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} @@ -8869,18 +8758,10 @@ packages: stylis@4.3.6: resolution: {integrity: sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==} - superagent@10.3.0: - resolution: {integrity: sha512-B+4Ik7ROgVKrQsXTV0Jwp2u+PXYLSlqtDAhYnkkD+zn3yg8s/zjA2MeGayPoY/KICrbitwneDHrjSotxKL+0XQ==} - engines: {node: '>=14.18.0'} - supertap@3.0.1: resolution: {integrity: sha512-u1ZpIBCawJnO+0QePsEiOknOfCRq0yERxiAchT0i4li0WHNUJbf0evXXSXOcCAR4M8iMDoajXYmstm/qO81Isw==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - supertest@7.2.2: - resolution: {integrity: sha512-oK8WG9diS3DlhdUkcFn4tkNIiIbBx9lI2ClF8K+b2/m8Eyv47LSawxUzZQSNKUrVb2KsqeTDCcjAAVPYaSLVTA==} - engines: {node: '>=14.18.0'} - supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} @@ -9103,10 +8984,6 @@ packages: resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} engines: {node: '>= 0.6'} - type-is@2.0.1: - resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} - engines: {node: '>= 0.6'} - typed-array-buffer@1.0.3: resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} engines: {node: '>= 0.4'} @@ -12901,8 +12778,6 @@ snapshots: '@noble/hashes@1.4.0': {} - '@noble/hashes@1.8.0': {} - '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -13186,10 +13061,6 @@ snapshots: '@oxlint/binding-win32-x64-msvc@1.59.0': optional: true - '@paralleldrive/cuid2@2.3.1': - dependencies: - '@noble/hashes': 1.8.0 - '@peculiar/asn1-cms@2.6.1': dependencies: '@peculiar/asn1-schema': 2.6.0 @@ -13907,8 +13778,6 @@ snapshots: dependencies: '@types/node': 24.12.2 - '@types/cookiejar@2.1.5': {} - '@types/d3-array@3.2.2': {} '@types/d3-axis@3.0.6': @@ -14128,8 +13997,6 @@ snapshots: '@types/mdx@2.0.13': {} - '@types/methods@1.1.4': {} - '@types/mime@1.3.5': {} '@types/ms@2.1.0': {} @@ -14254,18 +14121,6 @@ snapshots: '@types/stack-utils@2.0.3': {} - '@types/superagent@8.1.9': - dependencies: - '@types/cookiejar': 2.1.5 - '@types/methods': 1.1.4 - '@types/node': 24.12.2 - form-data: 4.0.5 - - '@types/supertest@7.2.0': - dependencies: - '@types/methods': 1.1.4 - '@types/superagent': 8.1.9 - '@types/trusted-types@2.0.7': {} '@types/unist@2.0.11': {} @@ -14513,11 +14368,6 @@ snapshots: mime-types: 2.1.35 negotiator: 0.6.3 - accepts@2.0.0: - dependencies: - mime-types: 3.0.2 - negotiator: 1.0.0 - acorn-import-attributes@1.9.5(acorn@8.16.0): dependencies: acorn: 8.16.0 @@ -14687,8 +14537,6 @@ snapshots: arrify@3.0.0: {} - asap@2.0.6: {} - asn1js@3.0.7: dependencies: pvtsutils: 1.3.6 @@ -14934,20 +14782,6 @@ snapshots: transitivePeerDependencies: - supports-color - body-parser@2.2.2: - dependencies: - bytes: 3.1.2 - content-type: 1.0.5 - debug: 4.4.3 - http-errors: 2.0.1 - iconv-lite: 0.7.2 - on-finished: 2.4.1 - qs: 6.15.0 - raw-body: 3.0.2 - type-is: 2.0.1 - transitivePeerDependencies: - - supports-color - bonjour-service@1.3.0: dependencies: fast-deep-equal: 3.1.3 @@ -15276,8 +15110,6 @@ snapshots: common-tags@1.8.2: {} - component-emitter@1.3.1: {} - compressible@2.0.18: dependencies: mime-db: 1.54.0 @@ -15332,8 +15164,6 @@ snapshots: dependencies: safe-buffer: 5.2.1 - content-disposition@1.0.1: {} - content-type@1.0.5: {} convert-source-map@2.0.0: {} @@ -15342,12 +15172,8 @@ snapshots: cookie-signature@1.0.7: {} - cookie-signature@1.2.2: {} - cookie@0.7.2: {} - cookiejar@2.1.4: {} - copy-text-to-clipboard@3.2.2: {} copy-webpack-plugin@11.0.0(webpack@5.105.4(@swc/core@1.15.21)): @@ -15841,11 +15667,6 @@ snapshots: dependencies: dequal: 2.0.3 - dezalgo@1.0.4: - dependencies: - asap: 2.0.6 - wrappy: 1.0.2 - didyoumean@1.2.2: {} diff@8.0.4: {} @@ -16231,39 +16052,6 @@ snapshots: transitivePeerDependencies: - supports-color - express@5.2.1: - dependencies: - accepts: 2.0.0 - body-parser: 2.2.2 - content-disposition: 1.0.1 - content-type: 1.0.5 - cookie: 0.7.2 - cookie-signature: 1.2.2 - debug: 4.4.3 - depd: 2.0.0 - encodeurl: 2.0.0 - escape-html: 1.0.3 - etag: 1.8.1 - finalhandler: 2.1.1 - fresh: 2.0.0 - http-errors: 2.0.1 - merge-descriptors: 2.0.0 - mime-types: 3.0.2 - on-finished: 2.4.1 - once: 1.4.0 - parseurl: 1.3.3 - proxy-addr: 2.0.7 - qs: 6.15.0 - range-parser: 1.2.1 - router: 2.2.0 - send: 1.2.1 - serve-static: 2.2.1 - statuses: 2.0.2 - type-is: 2.0.1 - vary: 1.1.2 - transitivePeerDependencies: - - supports-color - extend-shallow@2.0.1: dependencies: is-extendable: 0.1.1 @@ -16292,8 +16080,6 @@ snapshots: fast-json-stable-stringify@2.1.0: {} - fast-safe-stringify@2.1.1: {} - fast-uri@3.1.0: {} fastq@1.20.1: @@ -16367,17 +16153,6 @@ snapshots: transitivePeerDependencies: - supports-color - finalhandler@2.1.1: - dependencies: - debug: 4.4.3 - encodeurl: 2.0.0 - escape-html: 1.0.3 - on-finished: 2.4.1 - parseurl: 1.3.3 - statuses: 2.0.2 - transitivePeerDependencies: - - supports-color - find-cache-dir@4.0.0: dependencies: common-path-prefix: 3.0.0 @@ -16420,20 +16195,12 @@ snapshots: format@0.2.2: {} - formidable@3.5.4: - dependencies: - '@paralleldrive/cuid2': 2.3.1 - dezalgo: 1.0.4 - once: 1.4.0 - forwarded@0.2.0: {} fraction.js@5.3.4: {} fresh@0.5.2: {} - fresh@2.0.0: {} - fs-extra@11.3.4: dependencies: graceful-fs: 4.2.11 @@ -18168,8 +17935,6 @@ snapshots: media-typer@0.3.0: {} - media-typer@1.1.0: {} - memfs@4.57.1(tslib@2.8.1): dependencies: '@jsonjoy.com/fs-core': 4.57.1(tslib@2.8.1) @@ -18193,8 +17958,6 @@ snapshots: merge-descriptors@1.0.3: {} - merge-descriptors@2.0.0: {} - merge-stream@2.0.0: {} merge2@1.4.1: {} @@ -18545,8 +18308,6 @@ snapshots: mime@1.6.0: {} - mime@2.6.0: {} - mime@3.0.0: {} mimic-fn@2.1.0: {} @@ -19050,8 +18811,6 @@ snapshots: path-to-regexp@3.3.0: {} - path-to-regexp@8.4.0: {} - path-type@4.0.0: {} pathe@2.0.3: {} @@ -19662,10 +19421,6 @@ snapshots: dependencies: side-channel: 1.1.0 - qs@6.15.0: - dependencies: - side-channel: 1.1.0 - quansync@0.2.11: {} query-registry@3.0.1: @@ -19704,13 +19459,6 @@ snapshots: iconv-lite: 0.4.24 unpipe: 1.0.0 - raw-body@3.0.2: - dependencies: - bytes: 3.1.2 - http-errors: 2.0.1 - iconv-lite: 0.7.2 - unpipe: 1.0.0 - rc@1.2.8: dependencies: deep-extend: 0.6.0 @@ -20072,16 +19820,6 @@ snapshots: points-on-curve: 0.2.0 points-on-path: 0.2.1 - router@2.2.0: - dependencies: - debug: 4.4.3 - depd: 2.0.0 - is-promise: 4.0.0 - parseurl: 1.3.3 - path-to-regexp: 8.4.0 - transitivePeerDependencies: - - supports-color - rtlcss@4.3.0: dependencies: escalade: 3.2.0 @@ -20199,22 +19937,6 @@ snapshots: transitivePeerDependencies: - supports-color - send@1.2.1: - dependencies: - debug: 4.4.3 - encodeurl: 2.0.0 - escape-html: 1.0.3 - etag: 1.8.1 - fresh: 2.0.0 - http-errors: 2.0.1 - mime-types: 3.0.2 - ms: 2.1.3 - on-finished: 2.4.1 - range-parser: 1.2.1 - statuses: 2.0.2 - transitivePeerDependencies: - - supports-color - serialize-error@7.0.1: dependencies: type-fest: 0.13.1 @@ -20254,15 +19976,6 @@ snapshots: transitivePeerDependencies: - supports-color - serve-static@2.2.1: - dependencies: - encodeurl: 2.0.0 - escape-html: 1.0.3 - parseurl: 1.3.3 - send: 1.2.1 - transitivePeerDependencies: - - supports-color - set-blocking@2.0.0: {} set-function-length@1.2.2: @@ -20628,20 +20341,6 @@ snapshots: stylis@4.3.6: {} - superagent@10.3.0: - dependencies: - component-emitter: 1.3.1 - cookiejar: 2.1.4 - debug: 4.4.3 - fast-safe-stringify: 2.1.1 - form-data: 4.0.5 - formidable: 3.5.4 - methods: 1.1.2 - mime: 2.6.0 - qs: 6.15.0 - transitivePeerDependencies: - - supports-color - supertap@3.0.1: dependencies: indent-string: 5.0.0 @@ -20649,14 +20348,6 @@ snapshots: serialize-error: 7.0.1 strip-ansi: 7.2.0 - supertest@7.2.2: - dependencies: - cookie-signature: 1.2.2 - methods: 1.1.2 - superagent: 10.3.0 - transitivePeerDependencies: - - supports-color - supports-color@7.2.0: dependencies: has-flag: 4.0.0 @@ -20839,12 +20530,6 @@ snapshots: media-typer: 0.3.0 mime-types: 2.1.35 - type-is@2.0.1: - dependencies: - content-type: 1.0.5 - media-typer: 1.1.0 - mime-types: 3.0.2 - typed-array-buffer@1.0.3: dependencies: call-bound: 1.0.4 -- 2.51.2