From 01f50cd7765a3b554602ddc3c390dddc6ed1a755 Mon Sep 17 00:00:00 2001 From: Tester Date: Mon, 26 Jan 2026 22:10:00 +0100 Subject: [PATCH 01/13] Add task-1 assignment --- .hyf/score.example.json | 5 - .hyf/test.sh | 12 +- .hyf/tester.js | 52 + examples/margarita.md | 132 +++ examples/martini.md | 305 +++++ task-1/task 1 files => output/.gitkeep | 0 package-lock.json | 1468 ++++++++++++++++++++++++ package.json | 16 + task-1/cocktail.js | 33 + tests/cocktail.test.js | 167 +++ tests/drinks.js | 53 + 11 files changed, 2228 insertions(+), 15 deletions(-) delete mode 100644 .hyf/score.example.json create mode 100644 .hyf/tester.js create mode 100644 examples/margarita.md create mode 100644 examples/martini.md rename task-1/task 1 files => output/.gitkeep (100%) create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 task-1/cocktail.js create mode 100644 tests/cocktail.test.js create mode 100644 tests/drinks.js diff --git a/.hyf/score.example.json b/.hyf/score.example.json deleted file mode 100644 index 8d931f5..0000000 --- a/.hyf/score.example.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "score": 75, - "pass": true, - "passingScore": 50 -} \ No newline at end of file diff --git a/.hyf/test.sh b/.hyf/test.sh index ee037fc..901a19a 100644 --- a/.hyf/test.sh +++ b/.hyf/test.sh @@ -1,13 +1,5 @@ #!/usr/bin/env bash set -euo pipefail -# Run your test scripts here. -# Auto grade tool will execute this file within the .hyf working directory. -# The result should be stored in score.json file with the format shown below. -cat << EOF > score.json -{ - "score": 0, - "pass": true, - "passingScore": 0 -} -EOF +# Run the Node.js tester and capture stdout/stderr to the expected output file +/usr/bin/env node tester.js diff --git a/.hyf/tester.js b/.hyf/tester.js new file mode 100644 index 0000000..4f5e2a0 --- /dev/null +++ b/.hyf/tester.js @@ -0,0 +1,52 @@ +import fs from 'fs/promises'; +import { startVitest } from 'vitest/node'; + +const __dirname = import.meta.dirname; +process.chdir(__dirname); + +const outputFile = 'temp.json'; + +await startVitest( + 'test', + [], // CLI filters + { + include: ['../**/*.test.js'], + reporters: ['json', 'default'], + outputFile: outputFile, + watch: false, + }, // override test config + {}, // override Vite config + {} // custom Vitest options +); + +const reportContent = await fs.readFile(outputFile, 'utf-8'); +await fs.unlink(outputFile).catch(() => {}); + +try { + const { testResults } = JSON.parse(reportContent); + let testCount = 0; + let passedCount = 0; + for (const result of testResults) { + for (const assertionResult of result.assertionResults) { + const { status } = assertionResult; + if (status == 'passed') { + passedCount++; + } + testCount++; + } + } + + const PASSING_SCORE = 50; + const totalScore = (passedCount / testCount) * 100; + + const results = { + score: totalScore, + pass: totalScore >= PASSING_SCORE, + passingScore: PASSING_SCORE, + }; + + await fs.writeFile('score.json', JSON.stringify(results, null, 2)); +} catch (error) { + console.error('Error parsing report JSON:', error); + process.exit(1); +} diff --git a/examples/margarita.md b/examples/margarita.md new file mode 100644 index 0000000..86483bc --- /dev/null +++ b/examples/margarita.md @@ -0,0 +1,132 @@ +# Cocktail Recipes + +## Margarita + +![Margarita](https://www.thecocktaildb.com/images/media/drink/5noda61589575158.jpg/medium) + +**Category**: Ordinary Drink + +**Alcoholic**: Yes + +### Ingredients + +- 1 1/2 oz Tequila +- 1/2 oz Triple sec +- 1 oz Lime juice +- Salt + +### Instructions + +Rub the rim of the glass with the lime slice to make the salt stick to it. Take care to moisten only the outer rim and sprinkle the salt on it. The salt should present to the lips of the imbiber and never mix into the cocktail. Shake the other ingredients with ice, then carefully pour into the glass. + +Serve in: Cocktail glass + +## Blue Margarita + +![Blue Margarita](https://www.thecocktaildb.com/images/media/drink/bry4qh1582751040.jpg/medium) + +**Category**: Ordinary Drink + +**Alcoholic**: Yes + +### Ingredients + +- 1 1/2 oz Tequila +- 1 oz Blue Curacao +- 1 oz Lime juice +- Coarse Salt + +### Instructions + +Rub rim of cocktail glass with lime juice. Dip rim in coarse salt. Shake tequila, blue curacao, and lime juice with ice, strain into the salt-rimmed glass, and serve. + +Serve in: Cocktail glass + +## Tommy's Margarita + +![Tommy's Margarita](https://www.thecocktaildb.com/images/media/drink/loezxn1504373874.jpg/medium) + +**Category**: Ordinary Drink + +**Alcoholic**: Yes + +### Ingredients + +- 4.5 cl Tequila +- 1.5 cl Lime Juice +- 2 spoons Agave syrup + +### Instructions + +Shake and strain into a chilled cocktail glass. + +Serve in: Old-Fashioned glass + +## Whitecap Margarita + +![Whitecap Margarita](https://www.thecocktaildb.com/images/media/drink/srpxxp1441209622.jpg/medium) + +**Category**: Other / Unknown + +**Alcoholic**: Yes + +### Ingredients + +- 1 cup Ice +- 2 oz Tequila +- 1/4 cup Cream of coconut +- 3 tblsp fresh Lime juice + +### Instructions + +Place all ingredients in a blender and blend until smooth. This makes one drink. + +Serve in: Margarita/Coupette glass + +## Strawberry Margarita + +![Strawberry Margarita](https://www.thecocktaildb.com/images/media/drink/tqyrpw1439905311.jpg/medium) + +**Category**: Ordinary Drink + +**Alcoholic**: Yes + +### Ingredients + +- 1/2 oz Strawberry schnapps +- 1 oz Tequila +- 1/2 oz Triple sec +- 1 oz Lemon juice +- 1 oz Strawberries +- Salt + +### Instructions + +Rub rim of cocktail glass with lemon juice and dip rim in salt. Shake schnapps, tequila, triple sec, lemon juice, and strawberries with ice, strain into the salt-rimmed glass, and serve. + +Serve in: Cocktail glass + +## Smashed Watermelon Margarita + +![Smashed Watermelon Margarita](https://www.thecocktaildb.com/images/media/drink/dztcv51598717861.jpg/medium) + +**Category**: Cocktail + +**Alcoholic**: Yes + +### Ingredients + +- 1/2 cup Watermelon +- 5 Mint +- 1/3 Cup Grapefruit Juice +- Juice of 1/2 Lime +- 1 shot Tequila +- Garnish with Watermelon +- Garnish with Mint + +### Instructions + +In a mason jar muddle the watermelon and 5 mint leaves together into a puree and strain. Next add the grapefruit juice, juice of half a lime and the tequila as well as some ice. Put a lid on the jar and shake. Pour into a glass and add more ice. Garnish with fresh mint and a small slice of watermelon. + +Serve in: Collins glass + diff --git a/examples/martini.md b/examples/martini.md new file mode 100644 index 0000000..43bbaf8 --- /dev/null +++ b/examples/martini.md @@ -0,0 +1,305 @@ +# Cocktail Recipes + +## Martini + +![Martini](https://www.thecocktaildb.com/images/media/drink/71t8581504353095.jpg/medium) + +**Category**: Cocktail + +**Alcoholic**: Yes + +### Ingredients + +- 1 2/3 oz Gin +- 1/3 oz Dry Vermouth +- 1 Olive + +### Instructions + +Straight: Pour all ingredients into mixing glass with ice cubes. Stir well. Strain in chilled martini cocktail glass. Squeeze oil from lemon peel onto the drink, or garnish with olive. + +Serve in: Cocktail glass + +## Dry Martini + +![Dry Martini](https://www.thecocktaildb.com/images/media/drink/6ck9yi1589574317.jpg/medium) + +**Category**: Cocktail + +**Alcoholic**: Yes + +### Ingredients + +- 1 2/3 oz Gin +- 1/3 oz Dry Vermouth +- 1 Olive + +### Instructions + +Straight: Pour all ingredients into mixing glass with ice cubes. Stir well. Strain in chilled martini cocktail glass. Squeeze oil from lemon peel onto the drink, or garnish with olive. + +Serve in: Cocktail glass + +## Kiwi Martini + +![Kiwi Martini](https://www.thecocktaildb.com/images/media/drink/bmxmyq1630407098.jpg/medium) + +**Category**: Cocktail + +**Alcoholic**: Yes + +### Ingredients + +- 1/2 Kiwi +- 1 tsp Sugar Syrup +- 1 1/2 oz Vodka +- Garnish with Kiwi + +### Instructions + +The kiwi martini is a very fun vodka cocktail and it is one of the best drinks that makes use of fresh fruit. Though there are a few recipes floating around, this is one of the easiest and it is an absolutely delightful green martini to drink. + +For this recipe, you'll simply muddle slices of kiwi with simple syrup, then shake it with vodka. It's a drink that anyone can mix up in minutes and a perfect cocktail to show off your favorite vodka. + +Serve in: Cocktail glass + +## Vodka Martini + +![Vodka Martini](https://www.thecocktaildb.com/images/media/drink/qyxrqw1439906528.jpg/medium) + +**Category**: Ordinary Drink + +**Alcoholic**: Yes + +### Ingredients + +- 1 1/2 oz Vodka +- 3/4 oz Dry Vermouth +- 1 Olive + +### Instructions + +Shake the vodka and vermouth together with a number of ice cubes, strain into a cocktail glass, add the olive and serve. + +Serve in: Cocktail glass + +## Dirty Martini + +![Dirty Martini](https://www.thecocktaildb.com/images/media/drink/vcyvpq1485083300.jpg/medium) + +**Category**: Cocktail + +**Alcoholic**: Yes + +### Ingredients + +- 70ml/2fl oz Vodka +- 1 tbsp Dry Vermouth +- 2 tbsp Olive Brine +- 1 wedge Lemon +- 1 Olive + +### Instructions + +Pour the vodka, dry vermouth and olive brine into a cocktail shaker with a handful of ice and shake well. +Rub the rim of a martini glass with the wedge of lemon. +Strain the contents of the cocktail shaker into the glass and add the olive. +A dirty Martini contains a splash of olive brine or olive juice and is typically garnished with an olive. + +Serve in: Cocktail glass + +## Abbey Martini + +![Abbey Martini](https://www.thecocktaildb.com/images/media/drink/2mcozt1504817403.jpg/medium) + +**Category**: Cocktail + +**Alcoholic**: Yes + +### Ingredients + +- 2 shots Gin +- 1 shot Sweet Vermouth +- 1 shot Orange Juice +- 3 dashes Angostura Bitters + +### Instructions + +Put all ingredients into a shaker and mix, then strain contents into a chilled cocktail glass. + +Serve in: Cocktail glass + +## French Martini + +![French Martini](https://www.thecocktaildb.com/images/media/drink/clth721504373134.jpg/medium) + +**Category**: Cocktail + +**Alcoholic**: Yes + +### Ingredients + +- 4.5 cl Vodka +- 1.5 cl Raspberry Liqueur +- 1.5 cl pineapple juice + +### Instructions + +Pour all ingredients into shaker with ice cubes. Shake well and strain into a chilled cocktail glass. Squeeze oil from lemon peel onto the drink. + +Serve in: Cocktail glass + +## Bellini Martini + +![Bellini Martini](https://www.thecocktaildb.com/images/media/drink/3h9wv51504389379.jpg/medium) + +**Category**: Ordinary Drink + +**Alcoholic**: Yes + +### Ingredients + +- 8 cubes Ice +- 3 oz Vodka +- 1.5 oz Peach nectar +- 1.5 oz Peach schnapps +- 1 Lemon peel + +### Instructions + +Add ice cubes to shaker. +Add vodka. +Add peach schnapps. +Add peach nectar. +Shake. +Strain into glass. +Add lemon twist peel. + +Serve in: Cocktail glass + +## Espresso Martini + +![Espresso Martini](https://www.thecocktaildb.com/images/media/drink/n0sx531504372951.jpg/medium) + +**Category**: Cocktail + +**Alcoholic**: Yes + +### Ingredients + +- 5 cl Vodka +- 1 cl Kahlua +- 1 dash Sugar syrup + +### Instructions + +Pour ingredients into shaker filled with ice, shake vigorously, and strain into chilled martini glass + +Serve in: Cocktail glass + +## Pornstar Martini + +![Pornstar Martini](https://www.thecocktaildb.com/images/media/drink/xjhjdf1630406071.jpg/medium) + +**Category**: Cocktail + +**Alcoholic**: Yes + +### Ingredients + +- 3 cl Vodka +- 3 cl Passoa +- 1 cl Passion fruit juice +- 1 cl Lime +- 1 shot Prosecco + +### Instructions + +Straight: Pour all ingredients into mixing glass with ice cubes. Shake well. Strain in chilled martini cocktail glass. Cut passion fruit in half and use as garnish. Pour prosecco into a chilled shot glass and serve alongside the martini. + +Serve in: Cocktail glass + +## Cosmopolitan Martini + +![Cosmopolitan Martini](https://www.thecocktaildb.com/images/media/drink/upxxpq1439907580.jpg/medium) + +**Category**: Cocktail + +**Alcoholic**: Yes + +### Ingredients + +- 1/2 oz Cointreau +- 1 oz Vodka +- Juice of 1/2 Lime +- 1 splash Cranberry juice + +### Instructions + +Pour all ingredients in mixing glass half filled with ice, shake and strain into chilled Martini glass. + +Serve in: Cocktail Glass + +## Salted Toffee Martini + +![Salted Toffee Martini](https://www.thecocktaildb.com/images/media/drink/3s6mlr1509551211.jpg/medium) + +**Category**: Cocktail + +**Alcoholic**: Yes + +### Ingredients + +- 50 ml Gin +- 30 ml Chocolate liqueur +- 15 ml Amaretto +- Garnish Chocolate Sauce +- Grated Salted Chocolate + +### Instructions + +Add ice, toffee gin, chocolate liqueur and Amaretto to a cocktail shaker and shake vigorously. +Pour the chocolate syrup into a saucer and dip the top of a martini glass into the sauce. Grate some of the Willie\'s sea salt chocolate into another saucer and dip the coated glass, so the flakes stick to the sauce, creating a chocolate rim! +Pour the contents of the shaker into your chocolatey glass and sprinkle with more grated chocolate - enjoy! + +Serve in: Cocktail glass + +## Passion Fruit Martini + +![Passion Fruit Martini](https://www.thecocktaildb.com/images/media/drink/6trfve1582473527.jpg/medium) + +**Category**: Cocktail + +**Alcoholic**: Yes + +### Ingredients + +- 1 shot Vodka +- 1/2 shot Sugar Syrup +- Full Glass Passion fruit juice + +### Instructions + +Pour all ingredients into a glass and stir. Garnish with half a passion fruit piece. + +Serve in: Cocktail glass + +## Ziemes Martini Apfelsaft + +![Ziemes Martini Apfelsaft](https://www.thecocktaildb.com/images/media/drink/xnzr2p1485619687.jpg/medium) + +**Category**: Ordinary Drink + +**Alcoholic**: Yes + +### Ingredients + +- 4 cl Vermouth +- 16 cl Apple juice + +### Instructions + +Serve without ice. At least the juice shold have room temperature. + +Serve in: Collins Glass + diff --git a/task-1/task 1 files b/output/.gitkeep similarity index 100% rename from task-1/task 1 files rename to output/.gitkeep diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..0b6093c --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1468 @@ +{ + "name": "core-assignment-week-10", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "core-assignment-week-10", + "version": "1.0.0", + "license": "ISC", + "devDependencies": { + "vitest": "^4.0.17" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz", + "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz", + "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz", + "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz", + "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz", + "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz", + "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz", + "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz", + "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz", + "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz", + "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz", + "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz", + "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz", + "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz", + "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz", + "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz", + "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz", + "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz", + "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz", + "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz", + "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz", + "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz", + "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz", + "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz", + "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz", + "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz", + "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.56.0.tgz", + "integrity": "sha512-LNKIPA5k8PF1+jAFomGe3qN3bbIgJe/IlpDBwuVjrDKrJhVWywgnJvflMt/zkbVNLFtF1+94SljYQS6e99klnw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.56.0.tgz", + "integrity": "sha512-lfbVUbelYqXlYiU/HApNMJzT1E87UPGvzveGg2h0ktUNlOCxKlWuJ9jtfvs1sKHdwU4fzY7Pl8sAl49/XaEk6Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.56.0.tgz", + "integrity": "sha512-EgxD1ocWfhoD6xSOeEEwyE7tDvwTgZc8Bss7wCWe+uc7wO8G34HHCUH+Q6cHqJubxIAnQzAsyUsClt0yFLu06w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.56.0.tgz", + "integrity": "sha512-1vXe1vcMOssb/hOF8iv52A7feWW2xnu+c8BV4t1F//m9QVLTfNVpEdja5ia762j/UEJe2Z1jAmEqZAK42tVW3g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.56.0.tgz", + "integrity": "sha512-bof7fbIlvqsyv/DtaXSck4VYQ9lPtoWNFCB/JY4snlFuJREXfZnm+Ej6yaCHfQvofJDXLDMTVxWscVSuQvVWUQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.56.0.tgz", + "integrity": "sha512-KNa6lYHloW+7lTEkYGa37fpvPq+NKG/EHKM8+G/g9WDU7ls4sMqbVRV78J6LdNuVaeeK5WB9/9VAFbKxcbXKYg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.56.0.tgz", + "integrity": "sha512-E8jKK87uOvLrrLN28jnAAAChNq5LeCd2mGgZF+fGF5D507WlG/Noct3lP/QzQ6MrqJ5BCKNwI9ipADB6jyiq2A==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.56.0.tgz", + "integrity": "sha512-jQosa5FMYF5Z6prEpTCCmzCXz6eKr/tCBssSmQGEeozA9tkRUty/5Vx06ibaOP9RCrW1Pvb8yp3gvZhHwTDsJw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.56.0.tgz", + "integrity": "sha512-uQVoKkrC1KGEV6udrdVahASIsaF8h7iLG0U0W+Xn14ucFwi6uS539PsAr24IEF9/FoDtzMeeJXJIBo5RkbNWvQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.56.0.tgz", + "integrity": "sha512-vLZ1yJKLxhQLFKTs42RwTwa6zkGln+bnXc8ueFGMYmBTLfNu58sl5/eXyxRa2RarTkJbXl8TKPgfS6V5ijNqEA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.56.0.tgz", + "integrity": "sha512-FWfHOCub564kSE3xJQLLIC/hbKqHSVxy8vY75/YHHzWvbJL7aYJkdgwD/xGfUlL5UV2SB7otapLrcCj2xnF1dg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.56.0.tgz", + "integrity": "sha512-z1EkujxIh7nbrKL1lmIpqFTc/sr0u8Uk0zK/qIEFldbt6EDKWFk/pxFq3gYj4Bjn3aa9eEhYRlL3H8ZbPT1xvA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.56.0.tgz", + "integrity": "sha512-iNFTluqgdoQC7AIE8Q34R3AuPrJGJirj5wMUErxj22deOcY7XwZRaqYmB6ZKFHoVGqRcRd0mqO+845jAibKCkw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.56.0.tgz", + "integrity": "sha512-MtMeFVlD2LIKjp2sE2xM2slq3Zxf9zwVuw0jemsxvh1QOpHSsSzfNOTH9uYW9i1MXFxUSMmLpeVeUzoNOKBaWg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.56.0.tgz", + "integrity": "sha512-in+v6wiHdzzVhYKXIk5U74dEZHdKN9KH0Q4ANHOTvyXPG41bajYRsy7a8TPKbYPl34hU7PP7hMVHRvv/5aCSew==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.56.0.tgz", + "integrity": "sha512-yni2raKHB8m9NQpI9fPVwN754mn6dHQSbDTwxdr9SE0ks38DTjLMMBjrwvB5+mXrX+C0npX0CVeCUcvvvD8CNQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.56.0.tgz", + "integrity": "sha512-zhLLJx9nQPu7wezbxt2ut+CI4YlXi68ndEve16tPc/iwoylWS9B3FxpLS2PkmfYgDQtosah07Mj9E0khc3Y+vQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.56.0.tgz", + "integrity": "sha512-MVC6UDp16ZSH7x4rtuJPAEoE1RwS8N4oK9DLHy3FTEdFoUTCFVzMfJl/BVJ330C+hx8FfprA5Wqx4FhZXkj2Kw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.56.0.tgz", + "integrity": "sha512-ZhGH1eA4Qv0lxaV00azCIS1ChedK0V32952Md3FtnxSqZTBTd6tgil4nZT5cU8B+SIw3PFYkvyR4FKo2oyZIHA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.56.0.tgz", + "integrity": "sha512-O16XcmyDeFI9879pEcmtWvD/2nyxR9mF7Gs44lf1vGGx8Vg2DRNx11aVXBEqOQhWb92WN4z7fW/q4+2NYzCbBA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.56.0.tgz", + "integrity": "sha512-LhN/Reh+7F3RCgQIRbgw8ZMwUwyqJM+8pXNT6IIJAqm2IdKkzpCh/V9EdgOMBKuebIrzswqy4ATlrDgiOwbRcQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.56.0.tgz", + "integrity": "sha512-kbFsOObXp3LBULg1d3JIUQMa9Kv4UitDmpS+k0tinPBz3watcUiV2/LUDMMucA6pZO3WGE27P7DsfaN54l9ing==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.56.0.tgz", + "integrity": "sha512-vSSgny54D6P4vf2izbtFm/TcWYedw7f8eBrOiGGecyHyQB9q4Kqentjaj8hToe+995nob/Wv48pDqL5a62EWtg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.56.0.tgz", + "integrity": "sha512-FeCnkPCTHQJFbiGG49KjV5YGW/8b9rrXAM2Mz2kiIoktq2qsJxRD5giEMEOD2lPdgs72upzefaUvS+nc8E3UzQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.56.0.tgz", + "integrity": "sha512-H8AE9Ur/t0+1VXujj90w0HrSOuv0Nq9r1vSZF2t5km20NTfosQsGGUXDaKdQZzwuLts7IyL1fYT4hM95TI9c4g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitest/expect": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.18.tgz", + "integrity": "sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.0.18", + "@vitest/utils": "4.0.18", + "chai": "^6.2.1", + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.0.18.tgz", + "integrity": "sha512-HhVd0MDnzzsgevnOWCBj5Otnzobjy5wLBe4EdeeFGv8luMsGcYqDuFRMcttKWZA5vVO8RFjexVovXvAM4JoJDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.0.18", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.0.18.tgz", + "integrity": "sha512-P24GK3GulZWC5tz87ux0m8OADrQIUVDPIjjj65vBXYG17ZeU3qD7r+MNZ1RNv4l8CGU2vtTRqixrOi9fYk/yKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.0.18.tgz", + "integrity": "sha512-rpk9y12PGa22Jg6g5M3UVVnTS7+zycIGk9ZNGN+m6tZHKQb7jrP7/77WfZy13Y/EUDd52NDsLRQhYKtv7XfPQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.0.18", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.0.18.tgz", + "integrity": "sha512-PCiV0rcl7jKQjbgYqjtakly6T1uwv/5BQ9SwBLekVg/EaYeQFPiXcgrC2Y7vDMA8dM1SUEAEV82kgSQIlXNMvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.0.18", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.0.18.tgz", + "integrity": "sha512-cbQt3PTSD7P2OARdVW3qWER5EGq7PHlvE+QfzSC0lbwO+xnt7+XH06ZzFjFRgzUX//JmpxrCu92VdwvEPlWSNw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.0.18.tgz", + "integrity": "sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.0.18", + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz", + "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.2", + "@esbuild/android-arm": "0.27.2", + "@esbuild/android-arm64": "0.27.2", + "@esbuild/android-x64": "0.27.2", + "@esbuild/darwin-arm64": "0.27.2", + "@esbuild/darwin-x64": "0.27.2", + "@esbuild/freebsd-arm64": "0.27.2", + "@esbuild/freebsd-x64": "0.27.2", + "@esbuild/linux-arm": "0.27.2", + "@esbuild/linux-arm64": "0.27.2", + "@esbuild/linux-ia32": "0.27.2", + "@esbuild/linux-loong64": "0.27.2", + "@esbuild/linux-mips64el": "0.27.2", + "@esbuild/linux-ppc64": "0.27.2", + "@esbuild/linux-riscv64": "0.27.2", + "@esbuild/linux-s390x": "0.27.2", + "@esbuild/linux-x64": "0.27.2", + "@esbuild/netbsd-arm64": "0.27.2", + "@esbuild/netbsd-x64": "0.27.2", + "@esbuild/openbsd-arm64": "0.27.2", + "@esbuild/openbsd-x64": "0.27.2", + "@esbuild/openharmony-arm64": "0.27.2", + "@esbuild/sunos-x64": "0.27.2", + "@esbuild/win32-arm64": "0.27.2", + "@esbuild/win32-ia32": "0.27.2", + "@esbuild/win32-x64": "0.27.2" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/fdir": { + "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" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/obug": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", + "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT" + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rollup": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.56.0.tgz", + "integrity": "sha512-9FwVqlgUHzbXtDg9RCMgodF3Ua4Na6Gau+Sdt9vyCN4RhHfVKX2DCHy3BjMLTDd47ITDhYAnTwGulWTblJSDLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.56.0", + "@rollup/rollup-android-arm64": "4.56.0", + "@rollup/rollup-darwin-arm64": "4.56.0", + "@rollup/rollup-darwin-x64": "4.56.0", + "@rollup/rollup-freebsd-arm64": "4.56.0", + "@rollup/rollup-freebsd-x64": "4.56.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.56.0", + "@rollup/rollup-linux-arm-musleabihf": "4.56.0", + "@rollup/rollup-linux-arm64-gnu": "4.56.0", + "@rollup/rollup-linux-arm64-musl": "4.56.0", + "@rollup/rollup-linux-loong64-gnu": "4.56.0", + "@rollup/rollup-linux-loong64-musl": "4.56.0", + "@rollup/rollup-linux-ppc64-gnu": "4.56.0", + "@rollup/rollup-linux-ppc64-musl": "4.56.0", + "@rollup/rollup-linux-riscv64-gnu": "4.56.0", + "@rollup/rollup-linux-riscv64-musl": "4.56.0", + "@rollup/rollup-linux-s390x-gnu": "4.56.0", + "@rollup/rollup-linux-x64-gnu": "4.56.0", + "@rollup/rollup-linux-x64-musl": "4.56.0", + "@rollup/rollup-openbsd-x64": "4.56.0", + "@rollup/rollup-openharmony-arm64": "4.56.0", + "@rollup/rollup-win32-arm64-msvc": "4.56.0", + "@rollup/rollup-win32-ia32-msvc": "4.56.0", + "@rollup/rollup-win32-x64-gnu": "4.56.0", + "@rollup/rollup-win32-x64-msvc": "4.56.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", + "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyrainbow": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.0.3.tgz", + "integrity": "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/vite": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", + "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "esbuild": "^0.27.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vitest": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.0.18.tgz", + "integrity": "sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.0.18", + "@vitest/mocker": "4.0.18", + "@vitest/pretty-format": "4.0.18", + "@vitest/runner": "4.0.18", + "@vitest/snapshot": "4.0.18", + "@vitest/spy": "4.0.18", + "@vitest/utils": "4.0.18", + "es-module-lexer": "^1.7.0", + "expect-type": "^1.2.2", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^3.10.0", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.0.3", + "vite": "^6.0.0 || ^7.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.0.18", + "@vitest/browser-preview": "4.0.18", + "@vitest/browser-webdriverio": "4.0.18", + "@vitest/ui": "4.0.18", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..197c991 --- /dev/null +++ b/package.json @@ -0,0 +1,16 @@ +{ + "name": "core-assignment-week-10", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "vitest" + }, + "keywords": [], + "author": "", + "license": "ISC", + "type": "module", + "devDependencies": { + "vitest": "^4.0.17" + } +} diff --git a/task-1/cocktail.js b/task-1/cocktail.js new file mode 100644 index 0000000..228ab63 --- /dev/null +++ b/task-1/cocktail.js @@ -0,0 +1,33 @@ +// API documentation: https://www.thecocktaildb.com/api.php + +import path from 'path'; + +const BASE_URL = 'https://www.thecocktaildb.com/api/json/v1/1'; + +// Add helper functions as needed here + +export async function main() { + if (process.argv.length < 3) { + console.log('Please provide a cocktail name as a command line argument.'); + return; + } + + const cocktailName = process.argv[2]; + const url = `${BASE_URL}/search.php?s=${cocktailName}`; + + const __dirname = import.meta.dirname; + const outPath = path.join(__dirname, `../output/${cocktailName}.md`); + + try { + // 1. Fetch data from the API at the given URL + // 2. Generate markdown content to match the examples + // 3. Write the generated content to a markdown file as given by outPath + } catch (error) { + // 4. Handle errors + } +} + +// Do not change the code below +if (!process.env.VITEST) { + main(); +} diff --git a/tests/cocktail.test.js b/tests/cocktail.test.js new file mode 100644 index 0000000..d45d9a8 --- /dev/null +++ b/tests/cocktail.test.js @@ -0,0 +1,167 @@ +import fs from 'fs/promises'; +import path from 'path'; +import { beforeAll, describe, expect, test, vi } from 'vitest'; + +import { main } from '../task-1/cocktail.js'; + +import { data } from './drinks.js'; + +let lines = []; + +beforeAll(async () => { + const originalArgv = process.argv; + process.argv = ['node', 'main.js', 'margarita']; // Example cocktail name + + global.fetch = vi.fn(() => + Promise.resolve({ + ok: true, + json: () => Promise.resolve(data), + }) + ); + + // Mock console.log to suppress output during test + const originalConsoleLog = console.log; + console.log = () => {}; + + const __dirname = import.meta.dirname; + const outPath = path.join(__dirname, `../output/margarita.md`); + await fs.unlink(outPath).catch(() => {}); + + await main(); + + // Restore original process.argv and console.log + process.argv = originalArgv; + console.log = originalConsoleLog; + + try { + const content = await fs.readFile(outPath, 'utf-8'); + lines = content.trim().split('\n'); + } catch (error) { + console.error('Error reading generated markdown file:', error); + } +}); + +describe('Content tests', () => { + test('Markdown file created', async () => { + expect(lines.length).toBeGreaterThan(0); + }); + + test('Content starts with # Cocktail Recipes', async () => { + expect(lines[0]).toBe('# Cocktail Recipes'); + }); + + test('Content includes drink name as ## Margarita', async () => { + expect(lines).toContain('## Margarita'); + }); + + test('Content includes medium drink image', async () => { + expect(lines).toContain( + '![Margarita](https://www.thecocktaildb.com/images/media/drink/5noda61589575158.jpg/medium)' + ); + }); + + test('Content includes category and alcoholic info', async () => { + expect(lines).toContain('**Category**: Ordinary Drink'); + expect(lines).toContain('**Alcoholic**: Yes'); + }); + + test('Content includes header ### Ingredients', async () => { + expect(lines).toContain('### Ingredients'); + }); + + test('Content includes ingredients list', async () => { + expect(lines).toContain('- 1 1/2 oz Tequila'); + expect(lines).toContain('- 1/2 oz Triple sec'); + expect(lines).toContain('- 1 oz Lime juice'); + expect(lines).toContain('- Salt'); + }); + + test('Content includes header ### Instructions', async () => { + expect(lines).toContain('### Instructions'); + }); + + test('Content includes instruction details', async () => { + expect(lines).toContain('Rub the rim of the glass.'); + }); + + test('Content includes glass type', async () => { + expect(lines).toContain('Serve in: Cocktail glass'); + }); +}); + +describe('Error handling', () => { + test('Calls console.error() if no cocktail name argument provided', async () => { + const originalArgv = process.argv; + process.argv = ['node', 'main.js']; // No cocktail name + + // Mock console.log to capture output + const consoleErrorMock = vi + .spyOn(console, 'error') + .mockImplementation(() => {}); + + await main(); + + expect(consoleErrorMock).toHaveBeenCalled(); + + // Restore original process.argv and console.log + process.argv = originalArgv; + consoleErrorMock.mockRestore(); + }); + + test('Calls console.error() if cocktail not found', async () => { + const originalArgv = process.argv; + process.argv = ['node', 'main.js', 'nonexistentcocktail']; // Example cocktail name + + global.fetch = vi.fn(() => + Promise.resolve({ + ok: true, + json: () => Promise.resolve({ drinks: null }), + }) + ); + + // Mock console.log to capture output + const consoleLogMock = vi + .spyOn(console, 'error') + .mockImplementation(() => {}); + + await main(); + + expect(consoleLogMock).toHaveBeenCalledWith( + expect.stringContaining('No cocktails found with that name.') + ); + + // Restore original process.argv and console.log + process.argv = originalArgv; + consoleLogMock.mockRestore(); + + expect(fetch).toHaveBeenCalledTimes(1); + }); + + test('Calls console.error() if fetch was not OK', async () => { + const originalArgv = process.argv; + process.argv = ['node', 'main.js', 'margarita']; // Example cocktail name + + global.fetch = vi.fn(() => + Promise.resolve({ + ok: false, + status: 500, + json: () => Promise.resolve({}), + }) + ); + + // Mock console.log to suppress output during test + const consoleErrorMock = vi + .spyOn(console, 'error') + .mockImplementation(() => {}); + + await main(); + + expect(consoleErrorMock).toHaveBeenCalled(); + + // Restore original process.argv and console.log + process.argv = originalArgv; + consoleErrorMock.mockRestore(); + + expect(fetch).toHaveBeenCalledTimes(1); + }); +}); diff --git a/tests/drinks.js b/tests/drinks.js new file mode 100644 index 0000000..84d9b22 --- /dev/null +++ b/tests/drinks.js @@ -0,0 +1,53 @@ +export const data = { + drinks: [ + { + idDrink: '11007', + strDrink: 'Margarita', + strDrinkAlternate: null, + strTags: 'IBA,ContemporaryClassic', + strVideo: null, + strCategory: 'Ordinary Drink', + strIBA: 'Contemporary Classics', + strAlcoholic: 'Alcoholic', + strGlass: 'Cocktail glass', + strInstructions: 'Rub the rim of the glass.', + strDrinkThumb: + 'https://www.thecocktaildb.com/images/media/drink/5noda61589575158.jpg', + strIngredient1: 'Tequila', + strIngredient2: 'Triple sec', + strIngredient3: 'Lime juice', + strIngredient4: 'Salt', + strIngredient5: null, + strIngredient6: null, + strIngredient7: null, + strIngredient8: null, + strIngredient9: null, + strIngredient10: null, + strIngredient11: null, + strIngredient12: null, + strIngredient13: null, + strIngredient14: null, + strIngredient15: null, + strMeasure1: '1 1/2 oz ', + strMeasure2: '1/2 oz ', + strMeasure3: '1 oz ', + strMeasure4: null, + strMeasure5: null, + strMeasure6: null, + strMeasure7: null, + strMeasure8: null, + strMeasure9: null, + strMeasure10: null, + strMeasure11: null, + strMeasure12: null, + strMeasure13: null, + strMeasure14: null, + strMeasure15: null, + strImageSource: + 'https://commons.wikimedia.org/wiki/File:Klassiche_Margarita.jpg', + strImageAttribution: 'Cocktailmarler', + strCreativeCommonsConfirmed: 'Yes', + dateModified: '2015-08-18 14:42:59', + }, + ], +}; From 9ad5f88b933a388541075137eab9e2bb1ce838b4 Mon Sep 17 00:00:00 2001 From: Tester Date: Mon, 26 Jan 2026 22:20:30 +0100 Subject: [PATCH 02/13] Add npm install to test.sh --- .hyf/package-lock.json | 6 ++++++ .hyf/test.sh | 4 +++- 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 .hyf/package-lock.json diff --git a/.hyf/package-lock.json b/.hyf/package-lock.json new file mode 100644 index 0000000..15fb316 --- /dev/null +++ b/.hyf/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": ".hyf", + "lockfileVersion": 3, + "requires": true, + "packages": {} +} diff --git a/.hyf/test.sh b/.hyf/test.sh index 901a19a..84c902a 100644 --- a/.hyf/test.sh +++ b/.hyf/test.sh @@ -2,4 +2,6 @@ set -euo pipefail # Run the Node.js tester and capture stdout/stderr to the expected output file -/usr/bin/env node tester.js +cd .. +/usr/bin/env npm install +/usr/bin/env node .hyf/tester.js From ba6671e4676d36035ed8bd251ae8c9736bb4fb44 Mon Sep 17 00:00:00 2001 From: Tester Date: Mon, 26 Jan 2026 23:25:17 +0100 Subject: [PATCH 03/13] Output custom test results --- .hyf/test-output.txt | 21 +++++++++++++++++++++ .hyf/test.sh | 5 ++--- .hyf/tester.js | 22 ++++++++++++++-------- 3 files changed, 37 insertions(+), 11 deletions(-) create mode 100644 .hyf/test-output.txt diff --git a/.hyf/test-output.txt b/.hyf/test-output.txt new file mode 100644 index 0000000..051a845 --- /dev/null +++ b/.hyf/test-output.txt @@ -0,0 +1,21 @@ + +up to date, audited 42 packages in 493ms + +14 packages are looking for funding + run `npm fund` for details + +found 0 vulnerabilities +JSON report written to /Volumes/Crucial2TB/xdev/hackyourfuture/core-assignment-week-10/.hyf/report.json +❌ Markdown file created +❌ Content starts with # Cocktail Recipes +❌ Content includes drink name as ## Margarita +❌ Content includes medium drink image +❌ Content includes category and alcoholic info +❌ Content includes header ### Ingredients +❌ Content includes ingredients list +❌ Content includes header ### Instructions +❌ Content includes instruction details +❌ Content includes glass type +❌ Calls console.error() if no cocktail name argument provided +❌ Calls console.error() if cocktail not found +❌ Calls console.error() if fetch was not OK diff --git a/.hyf/test.sh b/.hyf/test.sh index 84c902a..a08eda2 100644 --- a/.hyf/test.sh +++ b/.hyf/test.sh @@ -2,6 +2,5 @@ set -euo pipefail # Run the Node.js tester and capture stdout/stderr to the expected output file -cd .. -/usr/bin/env npm install -/usr/bin/env node .hyf/tester.js +cd .. && /usr/bin/env npm install +cd .hyf && NO_COLOR=true /usr/bin/env node tester.js diff --git a/.hyf/tester.js b/.hyf/tester.js index 4f5e2a0..2f4586d 100644 --- a/.hyf/tester.js +++ b/.hyf/tester.js @@ -1,37 +1,43 @@ import fs from 'fs/promises'; import { startVitest } from 'vitest/node'; +import path from 'path'; const __dirname = import.meta.dirname; -process.chdir(__dirname); - -const outputFile = 'temp.json'; +const reportPath = path.join(__dirname, 'report.json'); +const reportFileName = 'report.json'; await startVitest( 'test', [], // CLI filters { include: ['../**/*.test.js'], - reporters: ['json', 'default'], - outputFile: outputFile, + reporters: ['json'], + outputFile: reportPath, watch: false, }, // override test config {}, // override Vite config {} // custom Vitest options ); -const reportContent = await fs.readFile(outputFile, 'utf-8'); -await fs.unlink(outputFile).catch(() => {}); +const reportContent = await fs.readFile(reportFileName, 'utf-8'); +await fs.unlink(reportFileName); try { const { testResults } = JSON.parse(reportContent); let testCount = 0; let passedCount = 0; + for (const result of testResults) { for (const assertionResult of result.assertionResults) { - const { status } = assertionResult; + const { title, status } = assertionResult; + let icon; if (status == 'passed') { + icon = '✅'; passedCount++; + } else { + icon = '❌'; } + console.log(`${icon} ${title}`); testCount++; } } From c58436733620b651989cb02c33a9764b48c8d02c Mon Sep 17 00:00:00 2001 From: Tester Date: Mon, 26 Jan 2026 23:29:54 +0100 Subject: [PATCH 04/13] Untrack test-output.txt --- .gitignore | 2 +- .hyf/test-output.txt | 21 --------------------- 2 files changed, 1 insertion(+), 22 deletions(-) delete mode 100644 .hyf/test-output.txt diff --git a/.gitignore b/.gitignore index 2b76d7c..4df3ef5 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ Thumbs.db # hyf .hyf/score.json +.hyf/test-output.txt # Editor and IDE settings .vscode/ @@ -155,4 +156,3 @@ dist # Vite logs files vite.config.js.timestamp-* vite.config.ts.timestamp-* - diff --git a/.hyf/test-output.txt b/.hyf/test-output.txt deleted file mode 100644 index 051a845..0000000 --- a/.hyf/test-output.txt +++ /dev/null @@ -1,21 +0,0 @@ - -up to date, audited 42 packages in 493ms - -14 packages are looking for funding - run `npm fund` for details - -found 0 vulnerabilities -JSON report written to /Volumes/Crucial2TB/xdev/hackyourfuture/core-assignment-week-10/.hyf/report.json -❌ Markdown file created -❌ Content starts with # Cocktail Recipes -❌ Content includes drink name as ## Margarita -❌ Content includes medium drink image -❌ Content includes category and alcoholic info -❌ Content includes header ### Ingredients -❌ Content includes ingredients list -❌ Content includes header ### Instructions -❌ Content includes instruction details -❌ Content includes glass type -❌ Calls console.error() if no cocktail name argument provided -❌ Calls console.error() if cocktail not found -❌ Calls console.error() if fetch was not OK From 47d3e71fa45902584ade1cf1fdb86aeb9ad20ee5 Mon Sep 17 00:00:00 2001 From: Tester Date: Mon, 26 Jan 2026 23:37:10 +0100 Subject: [PATCH 05/13] Remove exit on failure setting --- .hyf/test.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/.hyf/test.sh b/.hyf/test.sh index a08eda2..dd38784 100644 --- a/.hyf/test.sh +++ b/.hyf/test.sh @@ -1,5 +1,4 @@ #!/usr/bin/env bash -set -euo pipefail # Run the Node.js tester and capture stdout/stderr to the expected output file cd .. && /usr/bin/env npm install From 934d0111ad3fa32e9eec03df3c57ee75da0b2c04 Mon Sep 17 00:00:00 2001 From: Tester Date: Mon, 26 Jan 2026 23:41:24 +0100 Subject: [PATCH 06/13] Ensure exit 0 fropm test.sh --- .hyf/test.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/.hyf/test.sh b/.hyf/test.sh index dd38784..07c5b71 100644 --- a/.hyf/test.sh +++ b/.hyf/test.sh @@ -3,3 +3,4 @@ # Run the Node.js tester and capture stdout/stderr to the expected output file cd .. && /usr/bin/env npm install cd .hyf && NO_COLOR=true /usr/bin/env node tester.js +exit 0 From 850ee6400a467f784cede2bd0bc82d18ba37a9e2 Mon Sep 17 00:00:00 2001 From: Tester Date: Tue, 27 Jan 2026 13:15:08 +0100 Subject: [PATCH 07/13] Add per unit test scores --- .hyf/test.sh | 9 +++++++-- .hyf/tester.js | 25 ++++++++++++++++--------- tests/cocktail.test.js | 26 +++++++++++++------------- 3 files changed, 36 insertions(+), 24 deletions(-) diff --git a/.hyf/test.sh b/.hyf/test.sh index 07c5b71..4252a13 100644 --- a/.hyf/test.sh +++ b/.hyf/test.sh @@ -1,6 +1,11 @@ #!/usr/bin/env bash # Run the Node.js tester and capture stdout/stderr to the expected output file -cd .. && /usr/bin/env npm install -cd .hyf && NO_COLOR=true /usr/bin/env node tester.js +{ + pushd .. + /usr/bin/env npm install + popd +} >/dev/null + +NO_COLOR=true PASSING_SCORE=50 /usr/bin/env node tester.js exit 0 diff --git a/.hyf/tester.js b/.hyf/tester.js index 2f4586d..3e13cbb 100644 --- a/.hyf/tester.js +++ b/.hyf/tester.js @@ -1,6 +1,6 @@ import fs from 'fs/promises'; -import { startVitest } from 'vitest/node'; import path from 'path'; +import { startVitest } from 'vitest/node'; const __dirname = import.meta.dirname; const reportPath = path.join(__dirname, 'report.json'); @@ -24,31 +24,38 @@ await fs.unlink(reportFileName); try { const { testResults } = JSON.parse(reportContent); - let testCount = 0; - let passedCount = 0; + let maxPoints = 0; + let earnedPoints = 0; + + console.log('\nVitest unit test results:\n'); + + const passingScore = Number(process.env.PASSING_SCORE || 50); for (const result of testResults) { for (const assertionResult of result.assertionResults) { const { title, status } = assertionResult; + const match = title.match(/^\[(\d+)]/); + const points = match ? Number(match[1]) || 1 : 1; + maxPoints += points; let icon; if (status == 'passed') { icon = '✅'; - passedCount++; + earnedPoints += points; } else { icon = '❌'; } console.log(`${icon} ${title}`); - testCount++; } } - const PASSING_SCORE = 50; - const totalScore = (passedCount / testCount) * 100; + console.log(`\nScore: ${earnedPoints} of ${maxPoints}`); + + const totalScore = (earnedPoints / maxPoints) * 100; const results = { score: totalScore, - pass: totalScore >= PASSING_SCORE, - passingScore: PASSING_SCORE, + pass: totalScore >= passingScore, + passingScore: passingScore, }; await fs.writeFile('score.json', JSON.stringify(results, null, 2)); diff --git a/tests/cocktail.test.js b/tests/cocktail.test.js index d45d9a8..a71b162 100644 --- a/tests/cocktail.test.js +++ b/tests/cocktail.test.js @@ -42,55 +42,55 @@ beforeAll(async () => { }); describe('Content tests', () => { - test('Markdown file created', async () => { + test('[10] Markdown file created', async () => { expect(lines.length).toBeGreaterThan(0); }); - test('Content starts with # Cocktail Recipes', async () => { + test('[1] Content starts with # Cocktail Recipes', async () => { expect(lines[0]).toBe('# Cocktail Recipes'); }); - test('Content includes drink name as ## Margarita', async () => { + test('[1] Content includes drink name as ## Margarita', async () => { expect(lines).toContain('## Margarita'); }); - test('Content includes medium drink image', async () => { + test('[1] Content includes medium drink image', async () => { expect(lines).toContain( '![Margarita](https://www.thecocktaildb.com/images/media/drink/5noda61589575158.jpg/medium)' ); }); - test('Content includes category and alcoholic info', async () => { + test('[1] Content includes category and alcoholic info', async () => { expect(lines).toContain('**Category**: Ordinary Drink'); expect(lines).toContain('**Alcoholic**: Yes'); }); - test('Content includes header ### Ingredients', async () => { + test('[1] Content includes header ### Ingredients', async () => { expect(lines).toContain('### Ingredients'); }); - test('Content includes ingredients list', async () => { + test('[1] Content includes ingredients list', async () => { expect(lines).toContain('- 1 1/2 oz Tequila'); expect(lines).toContain('- 1/2 oz Triple sec'); expect(lines).toContain('- 1 oz Lime juice'); expect(lines).toContain('- Salt'); }); - test('Content includes header ### Instructions', async () => { + test('[1] Content includes header ### Instructions', async () => { expect(lines).toContain('### Instructions'); }); - test('Content includes instruction details', async () => { + test('[1] Content includes instruction details', async () => { expect(lines).toContain('Rub the rim of the glass.'); }); - test('Content includes glass type', async () => { + test('[1] Content includes glass type', async () => { expect(lines).toContain('Serve in: Cocktail glass'); }); }); describe('Error handling', () => { - test('Calls console.error() if no cocktail name argument provided', async () => { + test('[1] Calls console.error() if no cocktail name argument provided', async () => { const originalArgv = process.argv; process.argv = ['node', 'main.js']; // No cocktail name @@ -108,7 +108,7 @@ describe('Error handling', () => { consoleErrorMock.mockRestore(); }); - test('Calls console.error() if cocktail not found', async () => { + test('[1] Calls console.error() if cocktail not found', async () => { const originalArgv = process.argv; process.argv = ['node', 'main.js', 'nonexistentcocktail']; // Example cocktail name @@ -137,7 +137,7 @@ describe('Error handling', () => { expect(fetch).toHaveBeenCalledTimes(1); }); - test('Calls console.error() if fetch was not OK', async () => { + test('[1] Calls console.error() if fetch was not OK', async () => { const originalArgv = process.argv; process.argv = ['node', 'main.js', 'margarita']; // Example cocktail name From 01bf6bff6f6ed5f60efbacba4111ec52218a4d7c Mon Sep 17 00:00:00 2001 From: Tester Date: Tue, 27 Jan 2026 13:30:16 +0100 Subject: [PATCH 08/13] Add explanatory comments to test.sh --- .hyf/test.sh | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.hyf/test.sh b/.hyf/test.sh index 4252a13..c28ad61 100644 --- a/.hyf/test.sh +++ b/.hyf/test.sh @@ -1,11 +1,17 @@ #!/usr/bin/env bash -# Run the Node.js tester and capture stdout/stderr to the expected output file +# Run npm install silently { pushd .. /usr/bin/env npm install popd } >/dev/null -NO_COLOR=true PASSING_SCORE=50 /usr/bin/env node tester.js +# Run the Node.js tester and capture stdout/stderr to the expected output file. +# The passing score is read from the environment. +PASSING_SCORE=50 /usr/bin/env node tester.js + +# Vitest may return a non-zero exit code when one or more tests fail. +# This would terminate the GitHub workflow prematurely, therefore +# we need to explicitly set the exit code to zero. exit 0 From 5eed936e8bbe0ab7d481e768fe92e46950cd5ecb Mon Sep 17 00:00:00 2001 From: Tester Date: Tue, 27 Jan 2026 22:42:59 +0100 Subject: [PATCH 09/13] Code cleanup --- .hyf/tester.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/.hyf/tester.js b/.hyf/tester.js index 3e13cbb..d339fe0 100644 --- a/.hyf/tester.js +++ b/.hyf/tester.js @@ -4,7 +4,6 @@ import { startVitest } from 'vitest/node'; const __dirname = import.meta.dirname; const reportPath = path.join(__dirname, 'report.json'); -const reportFileName = 'report.json'; await startVitest( 'test', @@ -19,8 +18,8 @@ await startVitest( {} // custom Vitest options ); -const reportContent = await fs.readFile(reportFileName, 'utf-8'); -await fs.unlink(reportFileName); +const reportContent = await fs.readFile(reportPath, 'utf-8'); +await fs.unlink(reportPath); try { const { testResults } = JSON.parse(reportContent); @@ -32,13 +31,12 @@ try { const passingScore = Number(process.env.PASSING_SCORE || 50); for (const result of testResults) { - for (const assertionResult of result.assertionResults) { - const { title, status } = assertionResult; + for (const { title, status } of result.assertionResults) { const match = title.match(/^\[(\d+)]/); const points = match ? Number(match[1]) || 1 : 1; maxPoints += points; let icon; - if (status == 'passed') { + if (status === 'passed') { icon = '✅'; earnedPoints += points; } else { @@ -48,7 +46,7 @@ try { } } - console.log(`\nScore: ${earnedPoints} of ${maxPoints}`); + console.log(`\nRating: ${earnedPoints} of ${maxPoints}`); const totalScore = (earnedPoints / maxPoints) * 100; From c8ed475876f006592cbe53a119ffc34cac68b548 Mon Sep 17 00:00:00 2001 From: Tester Date: Wed, 28 Jan 2026 10:25:41 +0100 Subject: [PATCH 10/13] Move vitest execution ouf of tester.js --- .hyf/test.sh | 11 ++--------- .hyf/tester.js | 24 ++++++++---------------- 2 files changed, 10 insertions(+), 25 deletions(-) diff --git a/.hyf/test.sh b/.hyf/test.sh index c28ad61..85b02d8 100644 --- a/.hyf/test.sh +++ b/.hyf/test.sh @@ -1,17 +1,10 @@ #!/usr/bin/env bash -# Run npm install silently { pushd .. /usr/bin/env npm install - popd + npx vitest run --reporter=json --outputFile=.hyf/report.json + popd || exit } >/dev/null -# Run the Node.js tester and capture stdout/stderr to the expected output file. -# The passing score is read from the environment. PASSING_SCORE=50 /usr/bin/env node tester.js - -# Vitest may return a non-zero exit code when one or more tests fail. -# This would terminate the GitHub workflow prematurely, therefore -# we need to explicitly set the exit code to zero. -exit 0 diff --git a/.hyf/tester.js b/.hyf/tester.js index d339fe0..a7b5915 100644 --- a/.hyf/tester.js +++ b/.hyf/tester.js @@ -1,23 +1,9 @@ import fs from 'fs/promises'; import path from 'path'; -import { startVitest } from 'vitest/node'; const __dirname = import.meta.dirname; const reportPath = path.join(__dirname, 'report.json'); -await startVitest( - 'test', - [], // CLI filters - { - include: ['../**/*.test.js'], - reporters: ['json'], - outputFile: reportPath, - watch: false, - }, // override test config - {}, // override Vite config - {} // custom Vitest options -); - const reportContent = await fs.readFile(reportPath, 'utf-8'); await fs.unlink(reportPath); @@ -25,8 +11,10 @@ try { const { testResults } = JSON.parse(reportContent); let maxPoints = 0; let earnedPoints = 0; + let passedCount = 0; + let failedCount = 0; - console.log('\nVitest unit test results:\n'); + console.log('Vitest unit test results:\n'); const passingScore = Number(process.env.PASSING_SCORE || 50); @@ -38,15 +26,18 @@ try { let icon; if (status === 'passed') { icon = '✅'; + passedCount += 1; earnedPoints += points; } else { icon = '❌'; + failedCount += 1; } console.log(`${icon} ${title}`); } } - console.log(`\nRating: ${earnedPoints} of ${maxPoints}`); + console.log(`\nTotal passed: ${passedCount}`); + console.log(`Total failed: ${failedCount}`); const totalScore = (earnedPoints / maxPoints) * 100; @@ -57,6 +48,7 @@ try { }; await fs.writeFile('score.json', JSON.stringify(results, null, 2)); + process.exit(0); } catch (error) { console.error('Error parsing report JSON:', error); process.exit(1); From 07dc02ea7ec5b0769b579cd8d2cfd53af6298636 Mon Sep 17 00:00:00 2001 From: Tester Date: Wed, 28 Jan 2026 10:44:55 +0100 Subject: [PATCH 11/13] Move readFile into try block --- .hyf/tester.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.hyf/tester.js b/.hyf/tester.js index a7b5915..1245605 100644 --- a/.hyf/tester.js +++ b/.hyf/tester.js @@ -4,10 +4,10 @@ import path from 'path'; const __dirname = import.meta.dirname; const reportPath = path.join(__dirname, 'report.json'); -const reportContent = await fs.readFile(reportPath, 'utf-8'); -await fs.unlink(reportPath); - try { + const reportContent = await fs.readFile(reportPath, 'utf-8'); + await fs.unlink(reportPath); + const { testResults } = JSON.parse(reportContent); let maxPoints = 0; let earnedPoints = 0; From c18beab837ec23afa8c17c28a05e0929af11a7e6 Mon Sep 17 00:00:00 2001 From: Tester Date: Wed, 28 Jan 2026 17:09:34 +0100 Subject: [PATCH 12/13] Round total score --- .hyf/tester.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.hyf/tester.js b/.hyf/tester.js index 1245605..624a9cd 100644 --- a/.hyf/tester.js +++ b/.hyf/tester.js @@ -39,7 +39,7 @@ try { console.log(`\nTotal passed: ${passedCount}`); console.log(`Total failed: ${failedCount}`); - const totalScore = (earnedPoints / maxPoints) * 100; + const totalScore = Math.round((earnedPoints / maxPoints) * 100); const results = { score: totalScore, From a7f4bc3bc6aca9c8e39e60ce11aa1459a772f5e4 Mon Sep 17 00:00:00 2001 From: Tester Date: Wed, 28 Jan 2026 17:12:46 +0100 Subject: [PATCH 13/13] Fail one --- task-1/cocktail.js | 67 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 59 insertions(+), 8 deletions(-) diff --git a/task-1/cocktail.js b/task-1/cocktail.js index 228ab63..abc6fe4 100644 --- a/task-1/cocktail.js +++ b/task-1/cocktail.js @@ -1,14 +1,59 @@ // API documentation: https://www.thecocktaildb.com/api.php - +import fs from 'fs/promises'; import path from 'path'; const BASE_URL = 'https://www.thecocktaildb.com/api/json/v1/1'; -// Add helper functions as needed here +async function fetchData(url) { + const response = await fetch(url); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + const data = await response.json(); + return data; +} + +function createDrinkDetails(drink) { + const lines = []; + + lines.push(`## ${drink.strDrink}\n`); + if (drink.strDrinkThumb) { + lines.push(`![${drink.strDrink}](${drink.strDrinkThumb}/medium)\n`); + } + lines.push(`**Category**: ${drink.strCategory}\n\n`); + // lines.push( + // `**Alcoholic**: ${drink.strAlcoholic == 'Alcoholic' ? 'Yes' : 'No'}\n` + // ); + lines.push(`### Ingredients\n`); + for (let i = 1; i <= 15; i++) { + const ingredient = drink[`strIngredient${i}`]; + const measure = drink[`strMeasure${i}`]; + if (ingredient) { + if (measure) { + lines.push(`- ${measure.trim()} ${ingredient.trim()}`); + } else { + lines.push(`- ${ingredient.trim()}`); + } + } + } + lines.push(`\n### Instructions\n`); + lines.push(`${drink.strInstructions}\n`); + lines.push(`Serve in: ${drink.strGlass}\n`); + return lines; +} + +function createMarkdownContent(drinks) { + let lines = []; + lines.push(`# Cocktail Recipes\n`); + for (const drink of drinks) { + lines = lines.concat(createDrinkDetails(drink)); + } + return lines.join('\n'); +} export async function main() { if (process.argv.length < 3) { - console.log('Please provide a cocktail name as a command line argument.'); + console.error('Please provide a cocktail name as a command line argument.'); return; } @@ -19,15 +64,21 @@ export async function main() { const outPath = path.join(__dirname, `../output/${cocktailName}.md`); try { - // 1. Fetch data from the API at the given URL - // 2. Generate markdown content to match the examples - // 3. Write the generated content to a markdown file as given by outPath + const data = await fetchData(url); + + if (data.drinks) { + const content = createMarkdownContent(data.drinks); + await fs.writeFile(outPath, content, 'utf-8'); + + console.log(`Markdown file created for ${cocktailName}`); + } else { + console.error('No cocktails found with that name.'); + } } catch (error) { - // 4. Handle errors + console.error(`Error fetching data: ${error.message}`); } } -// Do not change the code below if (!process.env.VITEST) { main(); }