From 3b4ed6ef52829304fa073536c6f6270e4d08e363 Mon Sep 17 00:00:00 2001 From: agaes Date: Wed, 11 Feb 2026 14:03:05 +0800 Subject: [PATCH 01/26] feat(problem1): complete summation solutions and integrate endpoint - Implemented sum_to_n_a (for loop), sum_to_n_b (Array.reduce), and sum_to_n_c (recursion) - Exported all summation functions from summation_solution.js - Added /problem1 endpoint in index.js to return results from all three methods - Verified server responds with JSON output for summation results --- .gitignore | 16 + Index.js | 31 + package-lock.json | 1488 ++++++++++++++++++++++++++++ package.json | 28 + readme.md | 20 + src/problem1/summation_solution.js | 22 + 6 files changed, 1605 insertions(+) create mode 100644 .gitignore create mode 100644 Index.js create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 src/problem1/summation_solution.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..593261e1d --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +# Node.js dependencies +node_modules/ + +# Logs +*.log + +# Build output +dist/ + +# OS / editor files +.DS_Store +Thumbs.db +.vscode/ + +# Optional: lock file (depends on team policy) +package-lock.jsons \ No newline at end of file diff --git a/Index.js b/Index.js new file mode 100644 index 000000000..95f971550 --- /dev/null +++ b/Index.js @@ -0,0 +1,31 @@ +// index.js +import express from 'express'; +import { sum_to_n_a, sum_to_n_b, sum_to_n_c } from './src/problem1/summation_solution.js'; +const app = express(); +const PORT = process.env.PORT || 3000; + +// Middleware +app.use(express.json()); // for parsing JSON bodies +app.use(express.urlencoded({ extended: true })); // for parsing form data + +// Basic route +app.get('/problem1', (req, res) => { + + try{ + const results = { + forLoop: sum_to_n_a(5), + reducer: sum_to_n_b(5), + recursion: sum_to_n_c(5) + }; + + res.json(results); // sends results back to client + }catch(e){ + res.status(500).json(error); + } +}); + + +// Start server +app.listen(PORT, () => { + console.log(`Server running at http://localhost:${PORT}`); +}); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 000000000..4240f1aa9 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1488 @@ +{ + "name": "code-challenge", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "code-challenge", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "express": "^5.2.1" + }, + "devDependencies": { + "@types/express": "^5.0.6", + "@types/node": "^25.2.3", + "nodemon": "^3.1.11", + "ts-node": "^10.9.2", + "typescript": "^5.9.3" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "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/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz", + "integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz", + "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^5.0.0", + "@types/serve-static": "^2" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.1.tgz", + "integrity": "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "25.2.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.3.tgz", + "integrity": "sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", + "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*" + } + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true, + "license": "MIT" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/body-parser": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", + "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.1", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/content-disposition": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", + "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/diff": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", + "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "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/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true, + "license": "ISC" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/nodemon": { + "version": "3.1.11", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.11.tgz", + "integrity": "sha512-is96t8F/1//UHAjNPHpbsNY46ELPpftGUoSVNXwUfMk/qdjSylYrWSu1XavVTBOn526kFiOR733ATgNBCQyH0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true, + "license": "MIT" + }, + "node_modules/qs": { + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", + "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "license": "MIT", + "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" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/touch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", + "dev": true, + "license": "ISC", + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true, + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true, + "license": "MIT" + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 000000000..61b300040 --- /dev/null +++ b/package.json @@ -0,0 +1,28 @@ +{ + "name": "code-challenge", + "version": "1.0.0", + "description": "Note that if you fork this repository, your responses may be publicly linked to this repo. \r Please submit your application along with the solutions attached or linked.", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "start": "nodemon index.js" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/99techteam/code-challenge.git" + }, + "keywords": [], + "author": "", + "license": "ISC", + "type": "module", + "bugs": { + "url": "https://github.com/99techteam/code-challenge/issues" + }, + "homepage": "https://github.com/99techteam/code-challenge#readme", + "dependencies": { + "express": "^5.2.1" + }, + "devDependencies": { + "nodemo": "^1.0.0" + } +} diff --git a/readme.md b/readme.md index 1ff4bc95b..36d0b7006 100644 --- a/readme.md +++ b/readme.md @@ -8,3 +8,23 @@ It is important that you minimally attempt the problems, even if you do not arri ## Submission ## You can either provide a link to an online repository, attach the solution in your application, or whichever method you prefer. We're cool as long as we can view your solution without any pain. + + +# Code Challenge – Problem 1: Summations (Completed) + +## 📌 Overview +This project demonstrates three different approaches to solving the summation problem (sum of numbers from 1 to _n_): + +1. **For Loop** – Iterative summation +2. **Array.reduce** – Using JavaScript’s built-in reducer +3. **Recursion** – Recursive function calls + +The functions are exposed via an **Express.js server** with endpoints to test and view results +--- + +## 🚀 Features +- `sum_to_n_a(n)` → Summation using a `for` loop +- `sum_to_n_b(n)` → Summation using `Array.reduce` +- `sum_to_n_c(n)` → Summation using recursion +- Express server with: + - `/problem1` → Returns JSON results of all three summation methods \ No newline at end of file diff --git a/src/problem1/summation_solution.js b/src/problem1/summation_solution.js new file mode 100644 index 000000000..89b837550 --- /dev/null +++ b/src/problem1/summation_solution.js @@ -0,0 +1,22 @@ +function sum_to_n_a(n) { + let sum = 0; + for (let i = 1; i <= n; i++) { + sum += i; + } + return sum; +} + +function sum_to_n_b(n) { + const arr = Array.from({ length: n }, (_, i) => i + 1); + return arr.reduce((acc, curr) => acc + curr, 0); +} + +function sum_to_n_c(n) { + // Base case + if (n <= 1) { + return n; + } + return n + sum_to_n_c(n - 1); +} + +export { sum_to_n_a, sum_to_n_b, sum_to_n_c }; From 1f37f73daadfaede214d46bba51c2c8779b6f1fc Mon Sep 17 00:00:00 2001 From: agaes Date: Wed, 11 Feb 2026 17:03:20 +0800 Subject: [PATCH 02/26] fix: update problem1 to problem4 as requested --- Index.js | 4 ++-- src/{problem1 => problem4}/summation_solution.js | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename src/{problem1 => problem4}/summation_solution.js (100%) diff --git a/Index.js b/Index.js index 95f971550..958875d2f 100644 --- a/Index.js +++ b/Index.js @@ -1,6 +1,6 @@ // index.js import express from 'express'; -import { sum_to_n_a, sum_to_n_b, sum_to_n_c } from './src/problem1/summation_solution.js'; +import { sum_to_n_a, sum_to_n_b, sum_to_n_c } from './src/problem4/summation_solution.js'; const app = express(); const PORT = process.env.PORT || 3000; @@ -9,7 +9,7 @@ app.use(express.json()); // for parsing JSON bodies app.use(express.urlencoded({ extended: true })); // for parsing form data // Basic route -app.get('/problem1', (req, res) => { +app.get('/problem4', (req, res) => { try{ const results = { diff --git a/src/problem1/summation_solution.js b/src/problem4/summation_solution.js similarity index 100% rename from src/problem1/summation_solution.js rename to src/problem4/summation_solution.js From 5b8c1d922b3f74cb0231849d90c7f9eb308fcbaa Mon Sep 17 00:00:00 2001 From: agaes Date: Thu, 12 Feb 2026 12:03:52 +0800 Subject: [PATCH 03/26] Updated Package Json With necessary dependency like better-sqlite3 for database, express-router for handling user routing and nodemon in dev dependency to start the program in cmb USING npm run dev --- package-lock.json | 732 +++++++++++++++++++++++++++------------------- package.json | 6 +- 2 files changed, 429 insertions(+), 309 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4240f1aa9..f7894d1e1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,181 +9,12 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "express": "^5.2.1" + "better-sqlite3": "^12.6.2", + "express": "^5.2.1", + "express-router": "^0.0.1" }, "devDependencies": { - "@types/express": "^5.0.6", - "@types/node": "^25.2.3", - "nodemon": "^3.1.11", - "ts-node": "^10.9.2", - "typescript": "^5.9.3" - } - }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "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/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "node_modules/@tsconfig/node10": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz", - "integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true, - "license": "MIT" - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true, - "license": "MIT" - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/body-parser": { - "version": "1.19.6", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", - "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "node_modules/@types/connect": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", - "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/express": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz", - "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^5.0.0", - "@types/serve-static": "^2" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.1.tgz", - "integrity": "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "node_modules/@types/http-errors": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", - "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "25.2.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.3.tgz", - "integrity": "sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~7.16.0" - } - }, - "node_modules/@types/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/range-parser": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/send": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", - "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/serve-static": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz", - "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/http-errors": "*", - "@types/node": "*" + "nodemon": "^3.1.11" } }, "node_modules/accepts": { @@ -199,32 +30,6 @@ "node": ">= 0.6" } }, - "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.3.4", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", - "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "acorn": "^8.11.0" - }, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -239,13 +44,6 @@ "node": ">= 8" } }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true, - "license": "MIT" - }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -253,6 +51,40 @@ "dev": true, "license": "MIT" }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/better-sqlite3": { + "version": "12.6.2", + "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-12.6.2.tgz", + "integrity": "sha512-8VYKM3MjCa9WcaSAI3hzwhmyHVlH8tiGFwf0RlTsZPWJ1I5MkzjiudCo4KC4DxOaL/53A5B1sI/IbldNFDbsKA==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "bindings": "^1.5.0", + "prebuild-install": "^7.1.1" + }, + "engines": { + "node": "20.x || 22.x || 23.x || 24.x || 25.x" + } + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -266,6 +98,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "license": "MIT", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, "node_modules/body-parser": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", @@ -314,6 +166,30 @@ "node": ">=8" } }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -377,6 +253,12 @@ "fsevents": "~2.3.2" } }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "license": "ISC" + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -424,13 +306,6 @@ "node": ">=6.6.0" } }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true, - "license": "MIT" - }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -448,6 +323,30 @@ } } }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -457,14 +356,13 @@ "node": ">= 0.8" } }, - "node_modules/diff": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", - "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==", - "dev": true, - "license": "BSD-3-Clause", + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", "engines": { - "node": ">=0.3.1" + "node": ">=8" } }, "node_modules/dunder-proto": { @@ -496,6 +394,15 @@ "node": ">= 0.8" } }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", @@ -541,6 +448,15 @@ "node": ">= 0.6" } }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "license": "(MIT OR WTFPL)", + "engines": { + "node": ">=6" + } + }, "node_modules/express": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", @@ -584,6 +500,21 @@ "url": "https://opencollective.com/express" } }, + "node_modules/express-router": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/express-router/-/express-router-0.0.1.tgz", + "integrity": "sha512-6d8DAAUkhihgVQAOqo3lef+B/c9Q42mxKtt6O92fPmHotnv2TQvNClM+bUtTzX8q+V1v5YDyfFdZYTi3lMK/Vg==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "license": "MIT" + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -636,6 +567,12 @@ "node": ">= 0.8" } }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "license": "MIT" + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -697,6 +634,12 @@ "node": ">= 0.4" } }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "license": "MIT" + }, "node_modules/glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", @@ -792,6 +735,26 @@ "url": "https://opencollective.com/express" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/ignore-by-default": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", @@ -805,6 +768,12 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "license": "ISC" }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" + }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -866,13 +835,6 @@ "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", "license": "MIT" }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true, - "license": "ISC" - }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -928,6 +890,18 @@ "url": "https://opencollective.com/express" } }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -941,12 +915,33 @@ "node": "*" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "license": "MIT" + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, + "node_modules/napi-build-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", + "license": "MIT" + }, "node_modules/negotiator": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", @@ -956,6 +951,18 @@ "node": ">= 0.6" } }, + "node_modules/node-abi": { + "version": "3.87.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.87.0.tgz", + "integrity": "sha512-+CGM1L1CgmtheLcBuleyYOn7NWPVu0s0EJH2C4puxgEZb9h8QpR9G2dBfZJOAUhi7VQxuBPMd0hiISWcTyiYyQ==", + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/nodemon": { "version": "3.1.11", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.11.tgz", @@ -1060,6 +1067,32 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/prebuild-install": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -1080,10 +1113,20 @@ "dev": true, "license": "MIT" }, + "node_modules/pump": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", + "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "node_modules/qs": { - "version": "6.14.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", - "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", + "version": "6.14.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", + "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.1.0" @@ -1119,6 +1162,35 @@ "node": ">= 0.10" } }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -1148,6 +1220,26 @@ "node": ">= 18" } }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -1158,7 +1250,6 @@ "version": "7.7.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -1290,6 +1381,51 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, "node_modules/simple-update-notifier": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", @@ -1312,6 +1448,24 @@ "node": ">= 0.8" } }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -1325,6 +1479,34 @@ "node": ">=4" } }, + "node_modules/tar-fs": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", + "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", + "license": "MIT", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -1357,48 +1539,16 @@ "nodetouch": "bin/nodetouch.js" } }, - "node_modules/ts-node": { - "version": "10.9.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", - "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", - "dev": true, - "license": "MIT", + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "license": "Apache-2.0", "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" + "safe-buffer": "^5.0.1" }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } + "engines": { + "node": "*" } }, "node_modules/type-is": { @@ -1415,20 +1565,6 @@ "node": ">= 0.6" } }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, "node_modules/undefsafe": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", @@ -1436,13 +1572,6 @@ "dev": true, "license": "MIT" }, - "node_modules/undici-types": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", - "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", - "dev": true, - "license": "MIT" - }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -1452,11 +1581,10 @@ "node": ">= 0.8" } }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "license": "MIT" }, "node_modules/vary": { @@ -1473,16 +1601,6 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "license": "ISC" - }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } } } } diff --git a/package.json b/package.json index 61b300040..d27aad8a7 100644 --- a/package.json +++ b/package.json @@ -20,9 +20,11 @@ }, "homepage": "https://github.com/99techteam/code-challenge#readme", "dependencies": { - "express": "^5.2.1" + "better-sqlite3": "^12.6.2", + "express": "^5.2.1", + "express-router": "^0.0.1" }, "devDependencies": { - "nodemo": "^1.0.0" + "nodemon": "^3.1.11" } } From 43b4e19b87ad459d382f43002c4c3f4a5308c5d4 Mon Sep 17 00:00:00 2001 From: agaes Date: Thu, 12 Feb 2026 12:09:02 +0800 Subject: [PATCH 04/26] commiting database file --- ProgramDatabase.db | Bin 0 -> 4096 bytes ProgramDatabase.db-shm | Bin 0 -> 32768 bytes ProgramDatabase.db-wal | Bin 0 -> 86552 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 ProgramDatabase.db create mode 100644 ProgramDatabase.db-shm create mode 100644 ProgramDatabase.db-wal diff --git a/ProgramDatabase.db b/ProgramDatabase.db new file mode 100644 index 0000000000000000000000000000000000000000..159e90adb13561c0365c22e76084c658383a62f1 GIT binary patch literal 4096 zcmWFz^vNtqRY=P(%1ta$FlG>7U}9o$P*7lCU|@t|AVoG{WYFvV#S79dK(-m98b?E5 nGz3ONU^E0qLtr!nMnhmU1V%$(Gz3ONU^E0qLtr!nC=3Arqf-Z% literal 0 HcmV?d00001 diff --git a/ProgramDatabase.db-shm b/ProgramDatabase.db-shm new file mode 100644 index 0000000000000000000000000000000000000000..ea4c43c53e9de4da7da459b5b4cad104487cb1b5 GIT binary patch literal 32768 zcmeI)K`sPA6b9hGX9mLC+4Wj~UbX}Q0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!8Bg0$nzm z?oV?Bh7%ZNGuxSEo<)|!m2oqH1_j31%1(B(mxCNN*q@dN3@0$jH2dZCt@_@VAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ HpiO}r)YA5v{?njQ&#tsdYfPz*LAf<&8*LKXrfy7! z*o_Sx8*EI$$)?C)6UDx8Fvd3aVlNcb4fkTqO@!%i?7s-g+vZ5X0SN1y5$Vg+*(AF9218vO_ zZEagPEUutKR&I<0g00RW1KXqj{t$8XrKPSZUcXmWVu`5rY}(HpNG0v}9X3cug}rc# zuUcXH>n4Ba5BC@B=-74oc+H0|7s*PeD?zSN5I_I{1Q0*~0R#|0009ILKp;N_WFNtB zzd+FS`stQWdt%dNCH(?Yfr0=62q1s}0tg_000IagfIvPA$P7S1CSUNumg9>K6wiw? zULc=uIc-A#0R#|0009ILKmY**5I|s9z@8r<(=TwSc<-LSzuj^-^9$sfZ(#W3ydXOkh6T*dVj<gH++{w0RMI`ie)jp>pD@CP4D$Q6kkMWii>?sWMra-L3rwo{1@`VfJ~;RD zH(rtB1ys*bMf_0^KmY**5I_I{1Q0*~0R#|0;Jyhwr4}m10kuTegJ!DRdd7Jz+nVf7 z^d}Rmt!G6qQjb;EnT#)7T3ueD>w1;X=c}%%EU&CyUS7GpqEai@>-EZd-It?EG%IU< zfuVO#&edx#e=X-1P(4=^@kc=b0R#|0009ILKmY**5I_KdNh`3(9hf$LUxH{>*8BpS z{O^3R>cqJ>o$&&<6wj?m+i)%%0R#|0009ILKmY**5I_I{1ab-#s&4NzJ5nMxXFaFz zy8pWa3+8=X>+}okS3LW3o`6aO5I_I{1Q0*~0R#|0009ILm?(jmhz^tlN1_8-Fxl5- zCek?qd;P9+1T`W`FeXPJt3(rIj$rtC1SfxLA3C<#`>k@YU7(#>_8Z zR|&PO=M=7TZT+qL{Y#zB`~p`M&(%C~r6~v?fB*srAbT?%oLZUF8Vg zE1BAT+kfK%If4i9CUZ#$AbQT4Dgp#`I;kW_~wN9S<9X0Wsbn@8B#n$Bnc+r7oST;009ILKmY**5I_I{ z1Q5tif%$HqnyYK0;9lhj=6v6KZQhbyMdS$b^9w|)5I_I{1Q0*~0R#|0009ILP)2eD G1^)nGqh+xG literal 0 HcmV?d00001 From 5e3eeae2e1ed439fce8907f7c59a782912f99627 Mon Sep 17 00:00:00 2001 From: agaes Date: Thu, 12 Feb 2026 12:09:30 +0800 Subject: [PATCH 05/26] commiting database file --- ProgramDatabase.db-shm | Bin 32768 -> 32768 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/ProgramDatabase.db-shm b/ProgramDatabase.db-shm index ea4c43c53e9de4da7da459b5b4cad104487cb1b5..ff739aee7c51614862c3fbb7adaf59101cf9e234 100644 GIT binary patch delta 86 zcmZo@U}|V!;*@x#%K!!wIpqZp Date: Thu, 12 Feb 2026 12:14:41 +0800 Subject: [PATCH 06/26] feat(problem5): complete user CRUD implementation - Added CrudUser.js with full create, read, update, delete routes with filter capabilities in GET method - Integrated db.js for SQLite database connection - Updated index.js to mount routes at /problem5/users --- Index.js | 3 +- src/db.js | 35 ++++++++++++ src/problem5/.keep | 0 src/problem5/CrudUser.js | 113 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 150 insertions(+), 1 deletion(-) create mode 100644 src/db.js delete mode 100644 src/problem5/.keep create mode 100644 src/problem5/CrudUser.js diff --git a/Index.js b/Index.js index 958875d2f..761ea3909 100644 --- a/Index.js +++ b/Index.js @@ -1,6 +1,7 @@ // index.js import express from 'express'; import { sum_to_n_a, sum_to_n_b, sum_to_n_c } from './src/problem4/summation_solution.js'; +import CrudUser from '../code-challenge/src/problem5/CrudUser.js' const app = express(); const PORT = process.env.PORT || 3000; @@ -10,7 +11,6 @@ app.use(express.urlencoded({ extended: true })); // for parsing form data // Basic route app.get('/problem4', (req, res) => { - try{ const results = { forLoop: sum_to_n_a(5), @@ -24,6 +24,7 @@ app.get('/problem4', (req, res) => { } }); +app.use('/problem5/users', CrudUser); // Start server app.listen(PORT, () => { diff --git a/src/db.js b/src/db.js new file mode 100644 index 000000000..c1d9b9b7b --- /dev/null +++ b/src/db.js @@ -0,0 +1,35 @@ +import Database from 'better-sqlite3'; + +// Create or connect to the database file (creates foobar.db if missing) +const db = new Database('ProgramDatabase.db'); + +// Enable WAL mode for better concurrency +db.pragma('journal_mode = WAL'); + +// Initialize schema (create tables if they don’t exist) +db.prepare(` + CREATE TABLE IF NOT EXISTS users ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + email TEXT UNIQUE NOT NULL, + age INTEGER, + phone TEXT, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP + ) +`).run(); + +// Trigger to auto-update the updated_at field on changes +db.prepare(` + CREATE TRIGGER IF NOT EXISTS update_users_timestamps + AFTER UPDATE ON users + BEGIN + UPDATE users + SET created_at = CURRENT_TIMESTAMP, + updated_at = CURRENT_TIMESTAMP + WHERE id = NEW.id; + END; +`).run(); + +// Export the connection so other modules can use it +export default db; diff --git a/src/problem5/.keep b/src/problem5/.keep deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/problem5/CrudUser.js b/src/problem5/CrudUser.js new file mode 100644 index 000000000..f6e5c493a --- /dev/null +++ b/src/problem5/CrudUser.js @@ -0,0 +1,113 @@ +import express from 'express'; +import db from '../db.js' +const router = express.Router(); + +// CREATE user +router.post('/', (req, res) => { + try { + const { name, email, age, phone } = req.body; + const stmt = db.prepare('INSERT INTO users (name, email, age, phone) VALUES (?, ?, ?, ?)'); + const result = stmt.run(name, email, age, phone); + res.json({ id: result.lastInsertRowid, name, email, age, phone }); + } catch (error) { + res.status(500).json({ error: error.message }); + } +}); + +// READ all users +router.get('/', (req, res) => { + try { + const { name, email, age, phone } = req.query; + + let query = 'SELECT * FROM users WHERE 1=1'; + const params = []; + + if (name) { + query += ' AND name LIKE ?'; + params.push(`%${name}%`); + } + + if (email) { + query += ' AND email LIKE ?'; + params.push(`%${email}%`); + } + if (age) { + query += ' AND age = ?'; + params.push(age); + } + + if (phone) { + query += ' AND phone LIKE ?'; + params.push(`%${phone}%`); + } + + const stmt = db.prepare(query); + const rows = stmt.all(...params); + + res.json(rows); + + } catch (error) { + res.status(500).json({ error: error.message }); + } +}); + +// READ one user by id +router.get('/:id', (req, res) => { + try { + const { fields } = req.query; // e.g. ?fields=name,email + let query = 'SELECT * FROM users WHERE id = ?'; + + if (fields) { + const columns = fields.split(',').map(f => f.trim()).join(', '); + query = `SELECT ${columns} FROM users WHERE id = ?`; + } + + const stmt = db.prepare(query); + const user = stmt.get(req.params.id); + + if (user) { + res.json(user); + } else { + res.status(404).json({ error: 'User not found' }); + } + } catch (error) { + res.status(500).json({ error: error.message }); + } +}); + + +// UPDATE user +router.put('/:id', (req, res) => { + try { + const { name, email, age, phone } = req.body; + const stmt = db.prepare('UPDATE users SET name = ?, email = ?, age = ?, phone = ? WHERE id = ?'); + const result = stmt.run(name, email, age, phone, req.params.id); + if (result.changes > 0) { + res.json({ + message: 'User updated successfully', + user: { id: req.params.id, name, email, age, phone } + }); + } else { + res.status(404).json({ error: 'User not found' }); + } + } catch (error) { + res.status(500).json({ error: error.message }); + } +}); + +// DELETE user +router.delete('/:id', (req, res) => { + try { + const stmt = db.prepare('DELETE FROM users WHERE id = ?'); + const result = stmt.run(req.params.id); + if (result.changes > 0) { + res.json({ message: 'User deleted successfully' }); + } else { + res.status(404).json({ error: 'User not found' }); + } + } catch (error) { + res.status(500).json({ error: error.message }); + } +}); + +export default router; \ No newline at end of file From e75adf1887538918541bfefff7e550c6f5c2f0c2 Mon Sep 17 00:00:00 2001 From: agaes Date: Thu, 12 Feb 2026 12:45:09 +0800 Subject: [PATCH 07/26] Updated Readme.md for problem 5 and to instruct how to start server --- readme.md | 212 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 211 insertions(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 36d0b7006..138c8635d 100644 --- a/readme.md +++ b/readme.md @@ -27,4 +27,214 @@ The functions are exposed via an **Express.js server** with endpoints to test an - `sum_to_n_b(n)` → Summation using `Array.reduce` - `sum_to_n_c(n)` → Summation using recursion - Express server with: - - `/problem1` → Returns JSON results of all three summation methods \ No newline at end of file + - `/problem1` → Returns JSON results of all three summation methods + +Got it — let’s regenerate the documentation for **Problem 5** in a clean, professional style that matches your repo’s format. Here’s a polished section you can drop straight into your README: + +--- + +# Code Challenge – Problem 5: CRUD User Server (Completed) + +## 📌 Overview +This project implements a **CRUD (Create, Read, Update, Delete) API** for managing users, built with **Express.js** and **SQLite**. It provides endpoints to add, retrieve, update, and delete user records, with support for filtering and audit fields (`created_at`, `updated_at`). + +--- + +## 🚀 Features +- **Database Connection** + - `db.js` initializes a SQLite database using `better-sqlite3`. + - `users` table includes: + - `id` (primary key) + - `name`, `email`, `age`, `phone` + - `created_at` (auto‑set on insert) + - `updated_at` (auto‑updated on modification) + +- **CRUD Endpoints** + - `POST /problem5/users` → Create a new user + - `GET /problem5/users` → Retrieve all users (supports filters: `name`, `email`, `age`, `phone`) + - `GET /problem5/users/:id` → Retrieve a single user by ID (supports `?fields=` query to select specific columns) + - `PUT /problem5/users/:id` → Update an existing user by ID (refreshes `updated_at`) + - `DELETE /problem5/users/:id` → Delete a user by ID + +--- + +## 📂 Example Requests + +### Create User +```json +POST /problem5/users +{ + "name": "Agaesh Kumar", + "email": "agaesh.kumar@example.com", + "age": 25, + "phone": "+60123456789" +} +``` + +**Response:** +```json +{ + "id": 1, + "name": "Agaesh Kumar", + "email": "agaesh.kumar@example.com", + "age": 25, + "phone": "+60123456789", + "created_at": "2026-02-12 12:30:00", + "updated_at": "2026-02-12 12:30:00" +} +``` + +--- + +### Filter Users +```http +GET /problem5/users?name=agaesh&email=gmail.com +``` + +**Response:** +```json +[ + { + "id": 1, + "name": "Agaesh Kumar", + "email": "agaesh@gmail.com", + "age": 25, + "phone": "123456789", + "created_at": "2026-02-12 12:30:00", + "updated_at": "2026-02-12 12:30:00" + } +] +``` + +--- + +### Update User +```json +PUT /problem5/users/1 +{ + "name": "Agaesh K.", + "email": "agaesh.k@example.com", + "age": 26, + "phone": "987654321" +} +``` + +**Response:** +```json +{ + "message": "User updated successfully", + "user": { + "id": 1, + "name": "Agaesh K.", + "email": "agaesh.k@example.com", + "age": 26, + "phone": "987654321", + "created_at": "2026-02-12 12:30:00", + "updated_at": "2026-02-12 12:45:00" + } +} +``` + +--- + +### Delete User +```http +DELETE /problem5/users/1 +``` + +**Response:** +```json +{ + "message": "User deleted successfully" +} +``` + +--- + +# 🚀 Running the Server + +## 1. Start the Application +Make sure you’ve installed dependencies (`npm install`). +Then start the server with: + +```bash +npm start +``` + +You should see: + +``` +Server running at http://localhost:3000 +``` + +--- + +## 2. Problem 4 – Summation +Open your browser and visit: + +``` +http://localhost:3000/problem4 +``` + +You’ll see JSON output showing the results of the three summation methods: + +```json +{ + "forLoop": 15, + "reducer": 15, + "recursion": 15 +} +``` + +--- + +## 3. Problem 5 – CRUD Users + +### View All Users +Open in browser: + +``` +http://localhost:3000/problem5/users +``` + +This returns all users in the database. +You can also filter with query parameters: + +``` +http://localhost:3000/problem5/users?name=agaesh +``` + +--- + +### Create a User (POST) +Use **curl** or Postman to send a POST request: + +```bash +curl -X POST http://localhost:3000/problem5/users \ + -H "Content-Type: application/json" \ + -d '{"name":"Agaesh Kumar","email":"agaesh@example.com","age":25,"phone":"123456"}' +``` + +--- + +### Update a User (PUT) +```bash +curl -X PUT http://localhost:3000/problem5/users/1 \ + -H "Content-Type: application/json" \ + -d '{"name":"Agaesh K.","email":"agaesh.k@example.com","age":26,"phone":"987654321"}' +``` + +--- + +### Delete a User (DELETE) +```bash +curl -X DELETE http://localhost:3000/problem5/users/1 +``` + +--- + +## 4. Notes +- **Browser** is good for GET routes (`/problem4`, `/problem5/users`, `/problem5/users/:id`). +- **POST/PUT/DELETE** require tools like curl, Postman, or any REST client. +- `created_at` and `updated_at` fields are automatically managed by the database and included in responses. + From 062fc525705740b22ccb9ffb15ede2981c646808 Mon Sep 17 00:00:00 2001 From: agaes Date: Thu, 12 Feb 2026 13:22:05 +0800 Subject: [PATCH 08/26] Test case written to make sure create, Read and Update work fine --- Index.js | 12 +- ProgramDatabase.db-shm | Bin 32768 -> 32768 bytes ProgramDatabase.db-wal | Bin 86552 -> 259592 bytes package-lock.json | 1727 +++++++++++++++++++++++++++++++++++++++- package.json | 6 +- readme.md | 17 +- tests/problem5.test.js | 40 + 7 files changed, 1793 insertions(+), 9 deletions(-) create mode 100644 tests/problem5.test.js diff --git a/Index.js b/Index.js index 761ea3909..1bbb8870c 100644 --- a/Index.js +++ b/Index.js @@ -26,7 +26,11 @@ app.get('/problem4', (req, res) => { app.use('/problem5/users', CrudUser); -// Start server -app.listen(PORT, () => { - console.log(`Server running at http://localhost:${PORT}`); -}); \ No newline at end of file +// Start server +if (process.env.NODE_ENV !== 'test') { + app.listen(PORT, () => { + console.log(`Server running at http://localhost:${PORT}`); + }); +} + +export default app; \ No newline at end of file diff --git a/ProgramDatabase.db-shm b/ProgramDatabase.db-shm index ff739aee7c51614862c3fbb7adaf59101cf9e234..ef48ab86583c6c3d15b19b30f335c6b58f20aa43 100644 GIT binary patch delta 283 zcmZo@U}|V!s+V}A%K!q*K+MR%AYc!qS%A3a`jc;b{x`4MaO||b$-Qpo3%-(e)pLs- zNmUOt3Jh$4B;3{ng-3~~&L49X0u3>pkt z3_1+@42BHG3}y@#3|0)b4E7+kqAUy?4Ezj245ADY4Dt+W47v;^4AvVTel`XGFQH6V delta 183 zcmZo@U}|V!s+V}A%K!t63=9IIKn@EKKP!AF%v+-&X2Y@5_9pkbnTPWCFkbp)M3Hr#D z!50<+^pmPF$WNgKDTyViDPT{PLfFW@61B82H#5cJ1yf94nSy+!$*9Nz3)d{)+t*hX zCj>&gHy!4^=|DHM^Gk>`8#7|D5$wMZRTR%$RpQf#3%T(TZX^rc6A;UbG8KdILoW_5{ZU zNpO>On*H*ng7TmVjtxD*X-VZF08GhE@i~ zs6_!-2%6wHcuFHazfq|nDZ#Nr5*(Z7#7=fb&IV|LV@FSLeZM(_Z^yLm!ja&Bo+3ZN z;qw-9f)n=h-dXqAEQ6#32lgLYf_rG@sug-;Y66Y~M;Fgj?w34$ChM*bNeK>{XYx*0 zt+ov~DTO1!u?KNbkl;+MjL@nhLy!=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz", + "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz", + "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz", + "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz", + "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz", + "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz", + "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz", + "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz", + "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz", + "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz", + "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz", + "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz", + "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz", + "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz", + "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz", + "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz", + "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz", + "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz", + "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz", + "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz", + "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz", + "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz", + "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz", + "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz", + "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz", + "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==", + "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/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@paralleldrive/cuid2": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.3.1.tgz", + "integrity": "sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/hashes": "^1.1.5" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.57.1.tgz", + "integrity": "sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.57.1.tgz", + "integrity": "sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.57.1.tgz", + "integrity": "sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.57.1.tgz", + "integrity": "sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.57.1.tgz", + "integrity": "sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.57.1.tgz", + "integrity": "sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.57.1.tgz", + "integrity": "sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.57.1.tgz", + "integrity": "sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.57.1.tgz", + "integrity": "sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.57.1.tgz", + "integrity": "sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.57.1.tgz", + "integrity": "sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.57.1.tgz", + "integrity": "sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.57.1.tgz", + "integrity": "sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.57.1.tgz", + "integrity": "sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.57.1.tgz", + "integrity": "sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.57.1.tgz", + "integrity": "sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.57.1.tgz", + "integrity": "sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.57.1.tgz", + "integrity": "sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.57.1.tgz", + "integrity": "sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.57.1.tgz", + "integrity": "sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.57.1.tgz", + "integrity": "sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.57.1.tgz", + "integrity": "sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.57.1.tgz", + "integrity": "sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.57.1.tgz", + "integrity": "sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.57.1.tgz", + "integrity": "sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA==", + "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/accepts": { @@ -44,6 +1011,30 @@ "node": ">= 8" } }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "dev": true, + "license": "MIT" + }, + "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/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -228,6 +1219,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "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/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -259,6 +1260,29 @@ "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", "license": "ISC" }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/component-emitter": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz", + "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -306,6 +1330,13 @@ "node": ">=6.6.0" } }, + "node_modules/cookiejar": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", + "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", + "dev": true, + "license": "MIT" + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -347,6 +1378,16 @@ "node": ">=4.0.0" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -365,6 +1406,17 @@ "node": ">=8" } }, + "node_modules/dezalgo": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", + "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", + "dev": true, + "license": "ISC", + "dependencies": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -421,6 +1473,13 @@ "node": ">= 0.4" } }, + "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/es-object-atoms": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", @@ -433,12 +1492,80 @@ "node": ">= 0.4" } }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", + "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.3", + "@esbuild/android-arm": "0.27.3", + "@esbuild/android-arm64": "0.27.3", + "@esbuild/android-x64": "0.27.3", + "@esbuild/darwin-arm64": "0.27.3", + "@esbuild/darwin-x64": "0.27.3", + "@esbuild/freebsd-arm64": "0.27.3", + "@esbuild/freebsd-x64": "0.27.3", + "@esbuild/linux-arm": "0.27.3", + "@esbuild/linux-arm64": "0.27.3", + "@esbuild/linux-ia32": "0.27.3", + "@esbuild/linux-loong64": "0.27.3", + "@esbuild/linux-mips64el": "0.27.3", + "@esbuild/linux-ppc64": "0.27.3", + "@esbuild/linux-riscv64": "0.27.3", + "@esbuild/linux-s390x": "0.27.3", + "@esbuild/linux-x64": "0.27.3", + "@esbuild/netbsd-arm64": "0.27.3", + "@esbuild/netbsd-x64": "0.27.3", + "@esbuild/openbsd-arm64": "0.27.3", + "@esbuild/openbsd-x64": "0.27.3", + "@esbuild/openharmony-arm64": "0.27.3", + "@esbuild/sunos-x64": "0.27.3", + "@esbuild/win32-arm64": "0.27.3", + "@esbuild/win32-ia32": "0.27.3", + "@esbuild/win32-x64": "0.27.3" + } + }, "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", "license": "MIT" }, + "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/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", @@ -457,6 +1584,16 @@ "node": ">=6" } }, + "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/express": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", @@ -509,6 +1646,13 @@ "node": "*" } }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", + "dev": true, + "license": "MIT" + }, "node_modules/file-uri-to-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", @@ -549,6 +1693,64 @@ "url": "https://opencollective.com/express" } }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/form-data/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/form-data/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/formidable": { + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.4.tgz", + "integrity": "sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@paralleldrive/cuid2": "^2.2.2", + "dezalgo": "^1.0.4", + "once": "^1.4.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "url": "https://ko-fi.com/tunnckoCore/commissions" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -687,6 +1889,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -835,6 +2053,16 @@ "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", "license": "MIT" }, + "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/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -865,6 +2093,29 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/mime-db": { "version": "1.54.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", @@ -936,6 +2187,25 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, + "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/napi-build-utils": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", @@ -1014,6 +2284,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "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/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", @@ -1054,6 +2335,20 @@ "url": "https://opencollective.com/express" } }, + "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": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -1067,6 +2362,35 @@ "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/prebuild-install": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", @@ -1204,6 +2528,51 @@ "node": ">=8.10.0" } }, + "node_modules/rollup": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.57.1.tgz", + "integrity": "sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A==", + "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.57.1", + "@rollup/rollup-android-arm64": "4.57.1", + "@rollup/rollup-darwin-arm64": "4.57.1", + "@rollup/rollup-darwin-x64": "4.57.1", + "@rollup/rollup-freebsd-arm64": "4.57.1", + "@rollup/rollup-freebsd-x64": "4.57.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.57.1", + "@rollup/rollup-linux-arm-musleabihf": "4.57.1", + "@rollup/rollup-linux-arm64-gnu": "4.57.1", + "@rollup/rollup-linux-arm64-musl": "4.57.1", + "@rollup/rollup-linux-loong64-gnu": "4.57.1", + "@rollup/rollup-linux-loong64-musl": "4.57.1", + "@rollup/rollup-linux-ppc64-gnu": "4.57.1", + "@rollup/rollup-linux-ppc64-musl": "4.57.1", + "@rollup/rollup-linux-riscv64-gnu": "4.57.1", + "@rollup/rollup-linux-riscv64-musl": "4.57.1", + "@rollup/rollup-linux-s390x-gnu": "4.57.1", + "@rollup/rollup-linux-x64-gnu": "4.57.1", + "@rollup/rollup-linux-x64-musl": "4.57.1", + "@rollup/rollup-openbsd-x64": "4.57.1", + "@rollup/rollup-openharmony-arm64": "4.57.1", + "@rollup/rollup-win32-arm64-msvc": "4.57.1", + "@rollup/rollup-win32-ia32-msvc": "4.57.1", + "@rollup/rollup-win32-x64-gnu": "4.57.1", + "@rollup/rollup-win32-x64-msvc": "4.57.1", + "fsevents": "~2.3.2" + } + }, "node_modules/router": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", @@ -1381,6 +2750,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "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/simple-concat": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", @@ -1439,6 +2815,23 @@ "node": ">=10" } }, + "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/statuses": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", @@ -1448,6 +2841,13 @@ "node": ">= 0.8" } }, + "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/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -1466,6 +2866,42 @@ "node": ">=0.10.0" } }, + "node_modules/superagent": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-10.3.0.tgz", + "integrity": "sha512-B+4Ik7ROgVKrQsXTV0Jwp2u+PXYLSlqtDAhYnkkD+zn3yg8s/zjA2MeGayPoY/KICrbitwneDHrjSotxKL+0XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "component-emitter": "^1.3.1", + "cookiejar": "^2.1.4", + "debug": "^4.3.7", + "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.14.1" + }, + "engines": { + "node": ">=14.18.0" + } + }, + "node_modules/supertest": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.2.2.tgz", + "integrity": "sha512-oK8WG9diS3DlhdUkcFn4tkNIiIbBx9lI2ClF8K+b2/m8Eyv47LSawxUzZQSNKUrVb2KsqeTDCcjAAVPYaSLVTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cookie-signature": "^1.2.2", + "methods": "^1.1.2", + "superagent": "^10.3.0" + }, + "engines": { + "node": ">=14.18.0" + } + }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -1507,6 +2943,81 @@ "node": ">=6" } }, + "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/tinyglobby/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/tinyglobby/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", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "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/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -1596,6 +3107,220 @@ "node": ">= 0.8" } }, + "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", + "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/vite/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/vite/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", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "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/vitest/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", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "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" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/package.json b/package.json index d27aad8a7..035d83841 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "Note that if you fork this repository, your responses may be publicly linked to this repo. \r Please submit your application along with the solutions attached or linked.", "main": "index.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", + "test": "vitest", "start": "nodemon index.js" }, "repository": { @@ -25,6 +25,8 @@ "express-router": "^0.0.1" }, "devDependencies": { - "nodemon": "^3.1.11" + "nodemon": "^3.1.11", + "supertest": "^7.2.2", + "vitest": "^4.0.18" } } diff --git a/readme.md b/readme.md index 138c8635d..8fdebf5f2 100644 --- a/readme.md +++ b/readme.md @@ -154,8 +154,8 @@ DELETE /problem5/users/1 # 🚀 Running the Server ## 1. Start the Application -Make sure you’ve installed dependencies (`npm install`). -Then start the server with: + +Start the server with: ```bash npm start @@ -238,3 +238,16 @@ curl -X DELETE http://localhost:3000/problem5/users/1 - **POST/PUT/DELETE** require tools like curl, Postman, or any REST client. - `created_at` and `updated_at` fields are automatically managed by the database and included in responses. +--- + +## 1. Running TestCase + +Start the Server with: + +```bash +npm test +``` + +Writting the npm test in terminal will run the integration test + +--- diff --git a/tests/problem5.test.js b/tests/problem5.test.js new file mode 100644 index 000000000..7b3a023b5 --- /dev/null +++ b/tests/problem5.test.js @@ -0,0 +1,40 @@ +import { describe, it, expect } from 'vitest'; +import request from 'supertest'; +import app from '../Index'; // adjust path if needed + +describe('Problem 5 - CRUD Users (integration)', () => { + let userId; + + it('creates a user', async () => { + const res = await request(app) + .post('/problem5/users') + .send({ + name: 'Simple User', + email: 'simple@example.com', + age: 20, + phone: '123456' + }); + expect(res.status).toBe(200); + expect(res.body).toHaveProperty('id'); + userId = res.body.id; + }); + + it('updates a user', async () => { + const res = await request(app) + .put(`/problem5/users/${userId}`) + .send({ + name: 'Updated User', + email: 'updated@example.com', + age: 21, + phone: '987654' + }); + expect(res.status).toBe(200); + expect(res.body.user.name).toBe('Updated User'); + }); + + it('deletes a user', async () => { + const res = await request(app).delete(`/problem5/users/${userId}`); + expect(res.status).toBe(200); + expect(res.body.message).toBe('User deleted successfully'); + }); +}); \ No newline at end of file From adf3d0ce1d8b09c2bea4d72370831bef81c38b39 Mon Sep 17 00:00:00 2001 From: agaes Date: Thu, 12 Feb 2026 18:14:06 +0800 Subject: [PATCH 09/26] added condition to add score in existing table Users --- src/db.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/db.js b/src/db.js index c1d9b9b7b..da1f47dd1 100644 --- a/src/db.js +++ b/src/db.js @@ -25,11 +25,16 @@ db.prepare(` AFTER UPDATE ON users BEGIN UPDATE users - SET created_at = CURRENT_TIMESTAMP, - updated_at = CURRENT_TIMESTAMP + SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id; END; `).run(); +const pragma = db.prepare("PRAGMA table_info(users)").all(); +const hasScore = pragma.some(col => col.name === "score"); + +if (!hasScore) { + db.prepare("ALTER TABLE users ADD COLUMN score INTEGER DEFAULT 0").run(); +} // Export the connection so other modules can use it export default db; From d7973069a0d8d427f7ce0eb9178496ade3337adb Mon Sep 17 00:00:00 2001 From: agaes Date: Thu, 12 Feb 2026 18:14:52 +0800 Subject: [PATCH 10/26] - Installed and Used Websocket for live Update --- WebSocket.js | 21 +++++++++++++++++++++ package-lock.json | 24 +++++++++++++++++++++++- package.json | 3 ++- 3 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 WebSocket.js diff --git a/WebSocket.js b/WebSocket.js new file mode 100644 index 000000000..993b847f6 --- /dev/null +++ b/WebSocket.js @@ -0,0 +1,21 @@ +let wss; + +export function initWebSocket(server) { + wss = server; + + // Handle new connections + wss.on('connection', ws => { + console.log('Client connected to Problem 6'); + ws.send(JSON.stringify({ message: 'Connected to Problem 6 live updates' })); + }); +} + +export function broadcastProblem6(data) { + if (!wss) return; + + wss.clients.forEach(client => { + if (client.readyState === 1) { + client.send(JSON.stringify(data)); + } + }); +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 8b8faf4a7..8705e2ab2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,8 @@ "dependencies": { "better-sqlite3": "^12.6.2", "express": "^5.2.1", - "express-router": "^0.0.1" + "express-router": "^0.0.1", + "ws": "^8.19.0" }, "devDependencies": { "nodemon": "^3.1.11", @@ -3326,6 +3327,27 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "license": "ISC" + }, + "node_modules/ws": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", + "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } } } } diff --git a/package.json b/package.json index 035d83841..4748a2836 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,8 @@ "dependencies": { "better-sqlite3": "^12.6.2", "express": "^5.2.1", - "express-router": "^0.0.1" + "express-router": "^0.0.1", + "ws": "^8.19.0" }, "devDependencies": { "nodemon": "^3.1.11", From 0415139f077ee8b64200a86ed64e669ef4553601 Mon Sep 17 00:00:00 2001 From: agaes Date: Thu, 12 Feb 2026 22:00:29 +0800 Subject: [PATCH 11/26] Problem6: partial implementation of live leaderboard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Implemented scoreboard showing top 10 user scores. 2. Added live update via WebSocket broadcast. 3. User action increases score via API call. 4. API updates DB and dispatches leaderboard update. 5. Security/authorization not yet implemented. Files committed: - Client.js → listens on port 3000 for real-time leaderboard updates - src/problem/QuizAndAns.js → added quiz questions, options, and answers to drive score increments - src/problem/Architecture.js → defines Problem6 routes: • /problem6/leaderboard • /problem6/submit-quiz Packages used: - ws (WebSocket server/client) - better-sqlite3 (database) --- Client.js | 41 +++++ Index.js | 39 ++--- ProgramDatabase.db-shm | Bin 32768 -> 32768 bytes ProgramDatabase.db-wal | Bin 259592 -> 284312 bytes package-lock.json | 299 +++++++++++++++++++++++++++++++++++ package.json | 4 +- src/problem6/Architecture.js | 61 +++++++ src/problem6/QuizAndAns.js | 16 ++ 8 files changed, 441 insertions(+), 19 deletions(-) create mode 100644 Client.js create mode 100644 src/problem6/Architecture.js create mode 100644 src/problem6/QuizAndAns.js diff --git a/Client.js b/Client.js new file mode 100644 index 000000000..08e481f98 --- /dev/null +++ b/Client.js @@ -0,0 +1,41 @@ +// client.js +import WebSocket from 'ws'; + +function connect() { + const socket = new WebSocket('ws://localhost:3000'); // adjust port if needed + + socket.on('open', () => { + console.log('Connected to Problem 6 WebSocket'); + }); + + socket.on('message', (data) => { + try { + const msg = JSON.parse(data); + + if (msg.type === 'leaderboardUpdate') { + console.log('Leaderboard updated:'); + msg.leaderboard.forEach(user => { + console.log(`${user.name}: ${user.score}`); + }); + } else { + console.log('Received:', msg); + } + } catch (err) { + console.error('Failed to parse message:', data); + } + }); + + socket.on('close', () => { + console.log('Disconnected from WebSocket server, retrying in 5s...'); + setTimeout(connect, 5000); // retry after 5 seconds + }); + + socket.on('error', (err) => { + console.error('WebSocket error:', err.message); + // Close and retry + socket.close(); + }); +} + +// Initial connect attempt +connect(); \ No newline at end of file diff --git a/Index.js b/Index.js index 1bbb8870c..79d0e952d 100644 --- a/Index.js +++ b/Index.js @@ -1,36 +1,39 @@ -// index.js import express from 'express'; +import { WebSocketServer } from 'ws'; +import { initWebSocket } from './WebSocket.js'; + import { sum_to_n_a, sum_to_n_b, sum_to_n_c } from './src/problem4/summation_solution.js'; -import CrudUser from '../code-challenge/src/problem5/CrudUser.js' +import CrudUser from '../code-challenge/src/problem5/CrudUser.js'; +import Architecture from './src/problem6/Architecture.js'; + const app = express(); const PORT = process.env.PORT || 3000; -// Middleware -app.use(express.json()); // for parsing JSON bodies -app.use(express.urlencoded({ extended: true })); // for parsing form data +app.use(express.json()); +app.use(express.urlencoded({ extended: true })); -// Basic route app.get('/problem4', (req, res) => { - try{ + try { const results = { forLoop: sum_to_n_a(5), reducer: sum_to_n_b(5), recursion: sum_to_n_c(5) }; - - res.json(results); // sends results back to client - }catch(e){ - res.status(500).json(error); + res.json(results); + } catch (e) { + res.status(500).json({ error: e.message }); } }); app.use('/problem5/users', CrudUser); +app.use('/problem6', Architecture); + +const server = app.listen(PORT, () => { + console.log(`Server running at http://localhost:${PORT}`); +}); + +const wss = new WebSocketServer({ server }); -// Start server -if (process.env.NODE_ENV !== 'test') { - app.listen(PORT, () => { - console.log(`Server running at http://localhost:${PORT}`); - }); -} +initWebSocket(wss); -export default app; \ No newline at end of file +export { app }; diff --git a/ProgramDatabase.db-shm b/ProgramDatabase.db-shm index ef48ab86583c6c3d15b19b30f335c6b58f20aa43..595b4f9f0eaaaa0c33e9d17b4cdfc799b0aa2571 100644 GIT binary patch delta 201 zcmZo@U}|V!s+V}A%K!t63=9IUKn@EK3rk1&pKo(~V#Bf1_9pkbndiJe~jm delta 186 zcmZo@U}|V!s+V}A%K!q*K+MR%AYc!qS%A3a`jc;b{x`4MaO||b$-Qpo3%-(e)pLs- zNmUOt3Jh$46j&Cju3=E7w%wof_)AlC!x|t>)-TW;1KJN<-^vNtqRY=P( z%1ta$FlG>7U}9o$P*4D>Wd^ASVi;ham|`r-%%Io%iewP#l;z0>^Fbq<7br8Q7BH%FG^ML^b2uycMVc-admSH^$Afh(A>=F=fudu z|ASF#^LxHXMv4EWbxM!(r_1vKtzzM8VBnw6Z^3tuuK{Ro0iRq9Hz$LlIitFL4rEMEWMmYXZj;C;BxPx3YG`F-VPs%rrfXoNYiOik02SKKm&j>fR~Ruk%^N* z)s|7!*qCuL<3$@QLj#~QtqhEiTnZCnqJnRy1ATiC6myA_e_u#YMRBNql(g^@$F}pl z{1z~WPKO5dz&JDt6j&A@o3= 0.6" } }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -1230,6 +1255,43 @@ "node": ">=18" } }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -1261,6 +1323,38 @@ "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", "license": "ISC" }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -1291,6 +1385,54 @@ "dev": true, "license": "MIT" }, + "node_modules/concurrently": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.1.tgz", + "integrity": "sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==", + "license": "MIT", + "dependencies": { + "chalk": "4.1.2", + "rxjs": "7.8.2", + "shell-quote": "1.8.3", + "supports-color": "8.1.1", + "tree-kill": "1.2.2", + "yargs": "17.7.2" + }, + "bin": { + "conc": "dist/bin/concurrently.js", + "concurrently": "dist/bin/concurrently.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" + } + }, + "node_modules/concurrently/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/concurrently/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, "node_modules/content-disposition": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", @@ -1438,6 +1580,12 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", "license": "MIT" }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, "node_modules/encodeurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", @@ -1551,6 +1699,15 @@ "@esbuild/win32-x64": "0.27.3" } }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -1800,6 +1957,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -2025,6 +2191,15 @@ "node": ">=0.10.0" } }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -2529,6 +2704,15 @@ "node": ">=8.10.0" } }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/rollup": { "version": "4.57.1", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.57.1.tgz", @@ -2590,6 +2774,15 @@ "node": ">= 18" } }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -2679,6 +2872,18 @@ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "license": "ISC" }, + "node_modules/shell-quote": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", + "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/side-channel": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", @@ -2858,6 +3063,32 @@ "safe-buffer": "~5.2.0" } }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", @@ -3051,6 +3282,21 @@ "nodetouch": "bin/nodetouch.js" } }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, "node_modules/tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -3322,6 +3568,23 @@ "node": ">=8" } }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -3348,6 +3611,42 @@ "optional": true } } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "license": "ISC", + "engines": { + "node": ">=12" + } } } } diff --git a/package.json b/package.json index 4748a2836..28daa9d5c 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,8 @@ "main": "index.js", "scripts": { "test": "vitest", - "start": "nodemon index.js" + "start": "nodemon index.js", + "live": "concurrently \"npm start\" \"node client.js\"" }, "repository": { "type": "git", @@ -21,6 +22,7 @@ "homepage": "https://github.com/99techteam/code-challenge#readme", "dependencies": { "better-sqlite3": "^12.6.2", + "concurrently": "^9.2.1", "express": "^5.2.1", "express-router": "^0.0.1", "ws": "^8.19.0" diff --git a/src/problem6/Architecture.js b/src/problem6/Architecture.js new file mode 100644 index 000000000..07cff9738 --- /dev/null +++ b/src/problem6/Architecture.js @@ -0,0 +1,61 @@ +import express from 'express'; +import db from '../db.js'; +import { broadcastProblem6 } from '../../WebSocket.js'; +import quizQuestions from './QuizAndAns.js'; + +const router = express.Router(); + +router.get('/leaderboard', (req, res) => { + try { + const stmt = db.prepare("SELECT name, score FROM users LIMIT 10 ORDER BY score DESC"); + const users = stmt.all(); + + // Send via HTTP + res.status(200).json(users); + + // Broadcast only for Problem 6 clients + broadcastProblem6({ leaderboard: users }); + } catch (err) { + console.error(err); + res.status(500).json({ error: "Failed to fetch leaderboard" }); + } +}); + +router.post("/submit-quiz", (req, res) => { + try { + const { userId, answers } = req.body; + // answers = [{ questionId: 1, answer: "4" }, { questionId: 2, answer: "Paris" }] + + let points = 0; + answers.forEach(ans => { + const q = quizQuestions.find(q => q.id === ans.questionId); + if (q && String(q.correct).toLowerCase() === String(ans.answer).toLowerCase()) { + points += 1; + } + }); + + // Update DB with earned points + const result = db.prepare("UPDATE users SET score = score + ? WHERE id = ?").run(points, userId); + + // If no rows updated, user not found + if (result.changes === 0) { + return res.status(404).json({ error: "User not found" }); + } + + // Fetch updated leaderboard + const users = db.prepare("SELECT name, score FROM users ORDER BY score DESC").all(); + + // Broadcast live update + broadcastProblem6({ type: 'leaderboardUpdate', leaderboard: users }); + + res.json({ success: true, pointsEarned: points, leaderboard: users }); + } catch (err) { + console.error(err); + broadcastProblem6({ type: 'error', message: 'Quiz submission failed' }); + res.status(500).json({ error: "Failed to submit quiz" }); + } +}); + + + +export default router; diff --git a/src/problem6/QuizAndAns.js b/src/problem6/QuizAndAns.js new file mode 100644 index 000000000..cdc44c3b8 --- /dev/null +++ b/src/problem6/QuizAndAns.js @@ -0,0 +1,16 @@ +const quizQuestions = [ + { + id: 1, + question: "What is 2 + 2?", + options: ["3", "4", "5"], + correct: "4" + }, + { + id: 2, + question: "Capital of France?", + options: ["Berlin", "Paris", "Rome"], + correct: "Paris" + } +]; + +export default quizQuestions From c9d2982100f46ee77ffbf17b52a53f9f0d7cde05 Mon Sep 17 00:00:00 2001 From: agaes Date: Thu, 12 Feb 2026 22:22:33 +0800 Subject: [PATCH 12/26] Updated Readme.md for problem6 --- readme.md | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/readme.md b/readme.md index 8fdebf5f2..0b5a37d7a 100644 --- a/readme.md +++ b/readme.md @@ -251,3 +251,73 @@ npm test Writting the npm test in terminal will run the integration test --- + +# Code Challenge – Problem 6: Live Leaderboard (Partial) + +## 📌 Overview +Problem6 implements a **real-time scoreboard** that displays the top 10 user scores. +The system updates automatically whenever a user completes an action (e.g., submitting a quiz). + +### Features Implemented +1. **Scoreboard** – shows the top 10 users by score. +2. **Live updates** – leaderboard is broadcast via WebSocket to all connected clients. +3. **User actions** – completing an action increases the user’s score. +4. **API integration** – backend updates the database and dispatches leaderboard updates via broadcast. +5. **Security/authorization** – **not yet implemented** (malicious score inflation prevention still pending). + +--- + +## 🚀 Endpoints +- `GET /problem6/leaderboard` + Returns the current top 10 users and broadcasts the leaderboard to WebSocket clients. + +- `POST /problem6/submit-quiz` + Accepts quiz answers, calculates points, updates the user’s score, and broadcasts the updated leaderboard. + +--- + +## 📂 Files +- **Client.js** + WebSocket client that connects to `ws://localhost:3000` and listens for real-time leaderboard updates. + +- **src/problem/QuizAndAns.js** + Contains quiz questions, options, and correct answers used to calculate score increments. + +- **src/problem/Architecture.js** + Defines Problem6 routes: + - `/problem6/leaderboard` + - `/problem6/submit-quiz` + +--- + +## 📦 Packages Used +- **ws** – WebSocket server and client for real-time communication. +- **better-sqlite3** – lightweight SQLite database for storing and updating user scores. + +--- + +## 🛠️ Running Problem6 +1. Install dependencies: + ```bash + npm install + ``` +2. Start the backend: + ```bash + npm start + ``` +3. Run the WebSocket client (optional, for testing): + ```bash + npm run live + ``` + *(configured in `package.json` to run Server and `node client.js` Silmutaneously)* + +Note: Problem 6 uses the same user table used by problem 5. Users should be added first and then for problem 6, updating score through submit-quiz will trigger broadcast to send to all clients. to test it npm run live must be used. +--- + +## 🔜 Next Steps +- Implement **authorization and validation** to prevent malicious users from inflating scores. +- Add **audit logging** for score updates. +- Extend Client.js to display leaderboard visually in the browser. + +--- + From 2cbee9bba5fb243f7ab00019a02d843a0fad9c1c Mon Sep 17 00:00:00 2001 From: agaes Date: Thu, 12 Feb 2026 22:23:00 +0800 Subject: [PATCH 13/26] Refactored document to have some gaps between content --- readme.md | 1 + 1 file changed, 1 insertion(+) diff --git a/readme.md b/readme.md index 0b5a37d7a..aa62e8e80 100644 --- a/readme.md +++ b/readme.md @@ -312,6 +312,7 @@ The system updates automatically whenever a user completes an action (e.g., subm *(configured in `package.json` to run Server and `node client.js` Silmutaneously)* Note: Problem 6 uses the same user table used by problem 5. Users should be added first and then for problem 6, updating score through submit-quiz will trigger broadcast to send to all clients. to test it npm run live must be used. + --- ## 🔜 Next Steps From 66f87c42ddfa7d26fae9c8a9e4a041b27f035dec Mon Sep 17 00:00:00 2001 From: agaes Date: Thu, 12 Feb 2026 22:35:52 +0800 Subject: [PATCH 14/26] Fix: queried only top 10 in desc to update leaderboard instead of all users --- src/problem6/Architecture.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/problem6/Architecture.js b/src/problem6/Architecture.js index 07cff9738..afec8e9a6 100644 --- a/src/problem6/Architecture.js +++ b/src/problem6/Architecture.js @@ -43,7 +43,7 @@ router.post("/submit-quiz", (req, res) => { } // Fetch updated leaderboard - const users = db.prepare("SELECT name, score FROM users ORDER BY score DESC").all(); + const users = db.prepare("SELECT name, score FROM users LIMIT 10 ORDER BY score DESC").all(); // Broadcast live update broadcastProblem6({ type: 'leaderboardUpdate', leaderboard: users }); From 34ad5b6257bf5c22c4f56f9c76c06c30716cf7a2 Mon Sep 17 00:00:00 2001 From: agaes Date: Thu, 12 Feb 2026 22:43:46 +0800 Subject: [PATCH 15/26] Update flow of execution diagram in the readme.md --- readme.md | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/readme.md b/readme.md index aa62e8e80..bb9b9bbde 100644 --- a/readme.md +++ b/readme.md @@ -315,6 +315,53 @@ Note: Problem 6 uses the same user table used by problem 5. Users should be adde --- +Visual Diagram that Illustrate Flow Of Execution. + ++------------------+ +| | +| Client UI | +| (Browser/CLI) | +| | ++--------+---------+ + | + | 1. POST /problem6/submit-quiz + v ++---------------------------+ +| | +| API Server | +| (Architecture.js) | +| | ++--------+------------------+ + | + | 2. Save / update score + v ++---------------------------+ +| | +| Database | +| (better-sqlite3) | +| | ++--------+------------------+ + | + | 3. Read top 10 scores + | (trigger after update) + v ++---------------------------+ +| | +| WebSocket Server (ws) | +| | ++--------+------------------+ + | + | 4. Broadcast leaderboard + v ++---------------------------+ +| | +| Client Terminal / UI | +| (Client.js listening) | +| | ++---------------------------+ + +--- + ## 🔜 Next Steps - Implement **authorization and validation** to prevent malicious users from inflating scores. - Add **audit logging** for score updates. From f9b0acbf48568a18370be0483328fd9416ba6f84 Mon Sep 17 00:00:00 2001 From: agaes Date: Thu, 12 Feb 2026 22:45:25 +0800 Subject: [PATCH 16/26] Updated format of Visual Diagram --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index bb9b9bbde..7563553f5 100644 --- a/readme.md +++ b/readme.md @@ -315,7 +315,7 @@ Note: Problem 6 uses the same user table used by problem 5. Users should be adde --- -Visual Diagram that Illustrate Flow Of Execution. +## Visual Diagram that Illustrate Flow Of Execution. +------------------+ | | From 178e76d0f9b06739d5dcb0a716c5429617e1f592 Mon Sep 17 00:00:00 2001 From: agaes Date: Thu, 12 Feb 2026 23:55:13 +0800 Subject: [PATCH 17/26] problem6: Implemented security feature - JWT Token authorization for quiz submission - Added JWT-based middleware to authorize requests - Only authenticated users can submit quiz answers - Secret key stored securely using dotenv - Prevents unauthorized access and mitigates malware-style attacks --- Index.js | 5 ++ package-lock.json | 121 +++++++++++++++++++++++++++++++ package.json | 3 + src/middlewares/authorization.js | 20 +++++ src/problem6/Architecture.js | 7 +- 5 files changed, 154 insertions(+), 2 deletions(-) create mode 100644 src/middlewares/authorization.js diff --git a/Index.js b/Index.js index 79d0e952d..ebbc9eee4 100644 --- a/Index.js +++ b/Index.js @@ -6,6 +6,11 @@ import { sum_to_n_a, sum_to_n_b, sum_to_n_c } from './src/problem4/summation_sol import CrudUser from '../code-challenge/src/problem5/CrudUser.js'; import Architecture from './src/problem6/Architecture.js'; +import dotenv from 'dotenv'; + + +dotenv.config(); // loads .env file into process.env + const app = express(); const PORT = process.env.PORT || 3000; diff --git a/package-lock.json b/package-lock.json index ab874e47b..489aa7286 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,8 +11,11 @@ "dependencies": { "better-sqlite3": "^12.6.2", "concurrently": "^9.2.1", + "dotenv": "^17.2.4", "express": "^5.2.1", + "express-limit": "^1.0.0", "express-router": "^0.0.1", + "jsonwebtoken": "^9.0.3", "ws": "^8.19.0" }, "devDependencies": { @@ -1207,6 +1210,12 @@ "ieee754": "^1.1.13" } }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -1560,6 +1569,18 @@ "wrappy": "1" } }, + "node_modules/dotenv": { + "version": "17.2.4", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.4.tgz", + "integrity": "sha512-mudtfb4zRB4bVvdj0xRo+e6duH1csJRM8IukBqfTRvHotn9+LBXB8ynAidP9zHqoRC/fsllXgk4kCKlR21fIhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -1574,6 +1595,15 @@ "node": ">= 0.4" } }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -1795,6 +1825,12 @@ "url": "https://opencollective.com/express" } }, + "node_modules/express-limit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/express-limit/-/express-limit-1.0.0.tgz", + "integrity": "sha512-16WwC6LmLndCe2qUiJQWW7Z9RccKNjsl0pPfIWBeStm9XQYdcQ23dzQjo2ZO52fMYxVPuFtSOVn8qH/6h9kNmQ==", + "license": "ISC" + }, "node_modules/express-router": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/express-router/-/express-router-0.0.1.tgz", @@ -2229,6 +2265,91 @@ "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", "license": "MIT" }, + "node_modules/jsonwebtoken": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", + "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==", + "license": "MIT", + "dependencies": { + "jws": "^4.0.1", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jwa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", + "license": "MIT", + "dependencies": { + "jwa": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, "node_modules/magic-string": { "version": "0.30.21", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", diff --git a/package.json b/package.json index 28daa9d5c..2cad5a007 100644 --- a/package.json +++ b/package.json @@ -23,8 +23,11 @@ "dependencies": { "better-sqlite3": "^12.6.2", "concurrently": "^9.2.1", + "dotenv": "^17.2.4", "express": "^5.2.1", + "express-limit": "^1.0.0", "express-router": "^0.0.1", + "jsonwebtoken": "^9.0.3", "ws": "^8.19.0" }, "devDependencies": { diff --git a/src/middlewares/authorization.js b/src/middlewares/authorization.js new file mode 100644 index 000000000..e406d723d --- /dev/null +++ b/src/middlewares/authorization.js @@ -0,0 +1,20 @@ +// middleware/authMiddleware.js +import jwt from 'jsonwebtoken'; + +function Authorization(req, res, next) { + const authHeader = req.headers['authorization']; + if (!authHeader) { + return res.status(401).json({ error: 'You don’t have access to this resource' }); + } + + const token = authHeader.split(' ')[1]; + try { + const decoded = jwt.verify(token, process.env.JWT_KEY); // your secret from .env + req.userId = decoded.id; // attach userId to request + next(); + } catch (err) { + return res.status(403).json({ error: 'Invalid or expired token' }); + } +} + +export default Authorization; \ No newline at end of file diff --git a/src/problem6/Architecture.js b/src/problem6/Architecture.js index afec8e9a6..f4e22d0b4 100644 --- a/src/problem6/Architecture.js +++ b/src/problem6/Architecture.js @@ -2,6 +2,7 @@ import express from 'express'; import db from '../db.js'; import { broadcastProblem6 } from '../../WebSocket.js'; import quizQuestions from './QuizAndAns.js'; +import Authorization from '../middlewares/authorization.js'; const router = express.Router(); @@ -21,11 +22,13 @@ router.get('/leaderboard', (req, res) => { } }); -router.post("/submit-quiz", (req, res) => { +router.post("/submit-quiz", Authorization, (req, res) => { try { - const { userId, answers } = req.body; + const { answers } = req.body; // answers = [{ questionId: 1, answer: "4" }, { questionId: 2, answer: "Paris" }] + let userId = req.userId; + let points = 0; answers.forEach(ans => { const q = quizQuestions.find(q => q.id === ans.questionId); From 190404249064643669b458920f7ffa7bb8770ace Mon Sep 17 00:00:00 2001 From: agaes Date: Fri, 13 Feb 2026 00:09:16 +0800 Subject: [PATCH 18/26] created command for generating ,env and jwt secret key placeholder --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 2cad5a007..0e3ebb0b5 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,8 @@ "scripts": { "test": "vitest", "start": "nodemon index.js", - "live": "concurrently \"npm start\" \"node client.js\"" + "live": "concurrently \"npm start\" \"node client.js\"", + "env:init": "echo 'JWT_KEY=' > .env" }, "repository": { "type": "git", From 2800017baaadf246b0e6e7bce0a01764a4a744d4 Mon Sep 17 00:00:00 2001 From: agaes Date: Fri, 13 Feb 2026 00:20:19 +0800 Subject: [PATCH 19/26] - added helmet to prevent ClickJack, XSS and browser based attactk. it act as new layer of security --- Index.js | 3 ++- package-lock.json | 10 ++++++++++ package.json | 1 + 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Index.js b/Index.js index ebbc9eee4..963a49c6f 100644 --- a/Index.js +++ b/Index.js @@ -7,7 +7,7 @@ import CrudUser from '../code-challenge/src/problem5/CrudUser.js'; import Architecture from './src/problem6/Architecture.js'; import dotenv from 'dotenv'; - +import helmet from 'helmet'; dotenv.config(); // loads .env file into process.env @@ -16,6 +16,7 @@ const PORT = process.env.PORT || 3000; app.use(express.json()); app.use(express.urlencoded({ extended: true })); +app.use(helmet()); //to avoid clickjacking, XSS and browser based attacks app.get('/problem4', (req, res) => { try { diff --git a/package-lock.json b/package-lock.json index 489aa7286..2a06aaec4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "express": "^5.2.1", "express-limit": "^1.0.0", "express-router": "^0.0.1", + "helmet": "^8.1.0", "jsonwebtoken": "^9.0.3", "ws": "^8.19.0" }, @@ -2120,6 +2121,15 @@ "node": ">= 0.4" } }, + "node_modules/helmet": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-8.1.0.tgz", + "integrity": "sha512-jOiHyAZsmnr8LqoPGmCjYAaiuWwjAPLgY8ZX2XrmHawt99/u1y6RgrZMTeoPfpUbV96HOalYgz1qzkRbw54Pmg==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/http-errors": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", diff --git a/package.json b/package.json index 0e3ebb0b5..88859c9f3 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "express": "^5.2.1", "express-limit": "^1.0.0", "express-router": "^0.0.1", + "helmet": "^8.1.0", "jsonwebtoken": "^9.0.3", "ws": "^8.19.0" }, From 9e35e22066fa75c506b351d81c38551d275205dc Mon Sep 17 00:00:00 2001 From: agaes Date: Fri, 13 Feb 2026 01:06:46 +0800 Subject: [PATCH 20/26] problem4: security feature - add rate limiting - Introduced per-user and per-IP rate limiting to quiz submission routes in Architecture.js - Strengthened protection against abuse and spam by enforcing submission quotas - Removed outdated `express-limit` dependency - Added and configured `express-rate-limit` as the modern, secure replacement --- ProgramDatabase.db-shm | Bin 32768 -> 32768 bytes ProgramDatabase.db-wal | Bin 284312 -> 296672 bytes package-lock.json | 33 +++++++++++++++++++++++++------ package.json | 2 +- readme.md | 33 +++++++++++++++++++++++++++---- src/middlewares/authorization.js | 2 +- src/middlewares/ratelimiter.js | 28 ++++++++++++++++++++++++++ src/problem6/Architecture.js | 5 +++-- 8 files changed, 89 insertions(+), 14 deletions(-) create mode 100644 src/middlewares/ratelimiter.js diff --git a/ProgramDatabase.db-shm b/ProgramDatabase.db-shm index 595b4f9f0eaaaa0c33e9d17b4cdfc799b0aa2571..f96f0f38cafbb7eb7ea6ac24f5c383d98562ad31 100644 GIT binary patch delta 169 zcmZo@U}|V!s+V}A%K!t63=9GuKn@EK&o&H>$UBRTP yhnWpB_dgPViZd~IY;2TfWMtYrkulM6^FJpuW>z-_cLtBm3mFeGZT{uKW)1*Y^EK80 delta 165 zcmZo@U}|V!s+V}A%K!t63=9IUKn@EK3rk1&pKo(~V#Bf1_9pkbndiHOAw4Vx1Mc=@K!bzwQk0#qJ5y(*L?k~@lplR?&$QQg>>ary-VMpGF>Gb>Xw zDP%RU_%RI3sVd87M2-d06`oEjsO4v diff --git a/package-lock.json b/package-lock.json index 2a06aaec4..20a449855 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "concurrently": "^9.2.1", "dotenv": "^17.2.4", "express": "^5.2.1", - "express-limit": "^1.0.0", + "express-rate-limit": "^8.2.1", "express-router": "^0.0.1", "helmet": "^8.1.0", "jsonwebtoken": "^9.0.3", @@ -1826,11 +1826,23 @@ "url": "https://opencollective.com/express" } }, - "node_modules/express-limit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/express-limit/-/express-limit-1.0.0.tgz", - "integrity": "sha512-16WwC6LmLndCe2qUiJQWW7Z9RccKNjsl0pPfIWBeStm9XQYdcQ23dzQjo2ZO52fMYxVPuFtSOVn8qH/6h9kNmQ==", - "license": "ISC" + "node_modules/express-rate-limit": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.2.1.tgz", + "integrity": "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g==", + "license": "MIT", + "dependencies": { + "ip-address": "10.0.1" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": ">= 4.11" + } }, "node_modules/express-router": { "version": "0.0.1", @@ -2205,6 +2217,15 @@ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "license": "ISC" }, + "node_modules/ip-address": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz", + "integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", diff --git a/package.json b/package.json index 88859c9f3..6f09c505d 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "concurrently": "^9.2.1", "dotenv": "^17.2.4", "express": "^5.2.1", - "express-limit": "^1.0.0", + "express-rate-limit": "^8.2.1", "express-router": "^0.0.1", "helmet": "^8.1.0", "jsonwebtoken": "^9.0.3", diff --git a/readme.md b/readme.md index 7563553f5..7b4e6b12c 100644 --- a/readme.md +++ b/readme.md @@ -296,16 +296,41 @@ The system updates automatically whenever a user completes an action (e.g., subm --- -## 🛠️ Running Problem6 -1. Install dependencies: + +### 🛠️ Running Problem6 + +1. Install dependencies: ```bash npm install ``` -2. Start the backend: + +2. Initialize environment file (creates `.env` with placeholders): + + ```bash + npm run env:init + ``` + This will scaffold a `.env` file containing: + ``` + PORT=3000 + JWT_KEY= + ``` + +3. Generate a secure JWT key (fill in `jwt_key`): + + ```bash + node -e "console.log(require('crypto').randomBytes(32).toString('hex'))" + ``` + Copy the output and paste it into `.env`: + ``` + jwt_key=your_generated_secret_here + ``` + +4. Start the backend: ```bash npm start ``` -3. Run the WebSocket client (optional, for testing): + +5. Run the WebSocket client (optional, for testing): ```bash npm run live ``` diff --git a/src/middlewares/authorization.js b/src/middlewares/authorization.js index e406d723d..202d31d63 100644 --- a/src/middlewares/authorization.js +++ b/src/middlewares/authorization.js @@ -13,7 +13,7 @@ function Authorization(req, res, next) { req.userId = decoded.id; // attach userId to request next(); } catch (err) { - return res.status(403).json({ error: 'Invalid or expired token' }); + return res.status(403).json({ error: err}); } } diff --git a/src/middlewares/ratelimiter.js b/src/middlewares/ratelimiter.js new file mode 100644 index 000000000..0eb8e96ff --- /dev/null +++ b/src/middlewares/ratelimiter.js @@ -0,0 +1,28 @@ +// src/middleware/rateLimiter.js +import rateLimit, {ipKeyGenerator} from "express-rate-limit"; + +// Per-user limiter +export const userLimiter = rateLimit({ + windowMs: 60 * 1000, // 1 minute + limit: 2, // Max 2 submissions per user per minute + keyGenerator: (req) => req.userId || ipKeyGenerator(req), + standardHeaders: true, + legacyHeaders: false, + message: { + status: 429, + error: "Too many submissions. Limit is 2 per minute per user." + } +}); + +// Per-IP limiter +export const ipLimiter = rateLimit({ + windowMs: 60 * 1000, // 1 minute + limit: 2, // Max 2 submissions per IP per minute + keyGenerator: (req) => ipKeyGenerator, + standardHeaders: true, + legacyHeaders: false, + message: { + status: 429, + error: "Too many requests from this IP. Limit is 2 per minute." + } +}); \ No newline at end of file diff --git a/src/problem6/Architecture.js b/src/problem6/Architecture.js index f4e22d0b4..97b50f892 100644 --- a/src/problem6/Architecture.js +++ b/src/problem6/Architecture.js @@ -3,6 +3,7 @@ import db from '../db.js'; import { broadcastProblem6 } from '../../WebSocket.js'; import quizQuestions from './QuizAndAns.js'; import Authorization from '../middlewares/authorization.js'; +import { userLimiter, ipLimiter } from '../middlewares/ratelimiter.js'; const router = express.Router(); @@ -22,7 +23,7 @@ router.get('/leaderboard', (req, res) => { } }); -router.post("/submit-quiz", Authorization, (req, res) => { +router.post("/submit-quiz", Authorization, userLimiter, ipLimiter, (req, res) => { try { const { answers } = req.body; // answers = [{ questionId: 1, answer: "4" }, { questionId: 2, answer: "Paris" }] @@ -46,7 +47,7 @@ router.post("/submit-quiz", Authorization, (req, res) => { } // Fetch updated leaderboard - const users = db.prepare("SELECT name, score FROM users LIMIT 10 ORDER BY score DESC").all(); + const users = db.prepare("SELECT name, score FROM users ORDER BY score DESC LIMIT 10").all(); // Broadcast live update broadcastProblem6({ type: 'leaderboardUpdate', leaderboard: users }); From 20ccb06c4ad8de40d5f7741715ed8d0807cd0f79 Mon Sep 17 00:00:00 2001 From: agaes Date: Fri, 13 Feb 2026 01:07:34 +0800 Subject: [PATCH 21/26] commited gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 593261e1d..3265a6166 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,5 @@ Thumbs.db .vscode/ # Optional: lock file (depends on team policy) -package-lock.jsons \ No newline at end of file +package-lock.jsons +.env From 77e2f2eae06938bf23120fd65883b778455e8723 Mon Sep 17 00:00:00 2001 From: agaes Date: Fri, 13 Feb 2026 11:23:43 +0800 Subject: [PATCH 22/26] update README with instructions for using submit quiz Problem6 endpoints in router. --- image-1.png | Bin 0 -> 132071 bytes readme.md | 39 +++++++++++++++++++++++++++++++++++---- 2 files changed, 35 insertions(+), 4 deletions(-) create mode 100644 image-1.png diff --git a/image-1.png b/image-1.png new file mode 100644 index 0000000000000000000000000000000000000000..5592d29c41fc859d71c2123b34b84d356dcb8818 GIT binary patch literal 132071 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Ù~5)W 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%0A&92d!Mz0VLSrplApXu= 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≥<1SnVL-B>i9IShZ(L-0PQ(QkdXI} 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 + ``` + - Click **Send** to test. + +5. **Check the response** + - If the token is valid, you’ll see a success message with data (like `success: true`, `pointsEarned`, and `leaderboard`). --- @@ -387,8 +419,7 @@ Note: Problem 6 uses the same user table used by problem 5. Users should be adde --- -## 🔜 Next Steps -- Implement **authorization and validation** to prevent malicious users from inflating scores. +## 🔜 Next Steps - Add **audit logging** for score updates. - Extend Client.js to display leaderboard visually in the browser. From ed686f7feeedb070910a7497de43423df994bd4e Mon Sep 17 00:00:00 2001 From: agaes Date: Fri, 13 Feb 2026 12:32:01 +0800 Subject: [PATCH 23/26] feat(problem6): implement audit logging mechanism - Added auditLogger to record events into a file - Introduced getClientIp helper to normalize IPv4 addresses for logs - Integrated logging into quiz submission route for attempts, success, and errors - Applied logging in authorization middleware to capture access attempts and failures - Strengthened Problem 6 against potential misuse and suspicious activity --- src/middlewares/auditLogger.js | 18 ++++++++++++++++++ src/middlewares/authorization.js | 15 ++++++++++++--- src/middlewares/getClientIp.js | 12 ++++++++++++ src/problem6/Architecture.js | 9 +++++++-- 4 files changed, 49 insertions(+), 5 deletions(-) create mode 100644 src/middlewares/auditLogger.js create mode 100644 src/middlewares/getClientIp.js diff --git a/src/middlewares/auditLogger.js b/src/middlewares/auditLogger.js new file mode 100644 index 000000000..1cf8da212 --- /dev/null +++ b/src/middlewares/auditLogger.js @@ -0,0 +1,18 @@ +// auditLogger.js +import fs from 'fs'; +import path from 'path'; + +const logFile = path.join(process.cwd(), 'audit.log'); + +function logEvent(event, userId, ip) { + const entry = JSON.stringify({ + event, + userId, + ip, + timestamp: new Date().toISOString() + }) + '\n'; + + fs.appendFileSync(logFile, entry); +} + +export default logEvent; \ No newline at end of file diff --git a/src/middlewares/authorization.js b/src/middlewares/authorization.js index 202d31d63..4c3a85d48 100644 --- a/src/middlewares/authorization.js +++ b/src/middlewares/authorization.js @@ -1,19 +1,28 @@ // middleware/authMiddleware.js import jwt from 'jsonwebtoken'; +import logEvent from '../middlewares/auditLogger.js'; +import getClientIp from './getClientIp.js'; function Authorization(req, res, next) { const authHeader = req.headers['authorization']; + const ip = req.ip; if (!authHeader) { + logEvent("unauthorized_access_no_token", null, getClientIp(ip)); return res.status(401).json({ error: 'You don’t have access to this resource' }); } const token = authHeader.split(' ')[1]; try { - const decoded = jwt.verify(token, process.env.JWT_KEY); // your secret from .env - req.userId = decoded.id; // attach userId to request + const decoded = jwt.verify(token, process.env.JWT_KEY); + req.userId = decoded.id; + + // Successful authorization + logEvent("authorized_access", req.userId, getClientIp(ip)); + next(); } catch (err) { - return res.status(403).json({ error: err}); + logEvent("unauthorized_access_invalid_token", null, getClientIp(ip)); + return res.status(403).json({ error: "Invalid token" }); } } diff --git a/src/middlewares/getClientIp.js b/src/middlewares/getClientIp.js new file mode 100644 index 000000000..5e13761ee --- /dev/null +++ b/src/middlewares/getClientIp.js @@ -0,0 +1,12 @@ + +function getClientIp(ip) { + // Convert IPv6 localhost (::1) to IPv4 localhost + if (ip === '::1') return '127.0.0.1'; + + // Strip IPv6 prefix if it's an IPv4-mapped address (::ffff:192.168.1.10) + if (ip.startsWith('::ffff:')) return ip.split('::ffff:')[1]; + + return ip; +} + +export default getClientIp \ No newline at end of file diff --git a/src/problem6/Architecture.js b/src/problem6/Architecture.js index 97b50f892..26b1a3358 100644 --- a/src/problem6/Architecture.js +++ b/src/problem6/Architecture.js @@ -4,6 +4,8 @@ import { broadcastProblem6 } from '../../WebSocket.js'; import quizQuestions from './QuizAndAns.js'; import Authorization from '../middlewares/authorization.js'; import { userLimiter, ipLimiter } from '../middlewares/ratelimiter.js'; +import logEvent from '../middlewares/auditLogger.js'; +import getClientIp from '../middlewares/getClientIp.js'; const router = express.Router(); @@ -29,6 +31,7 @@ router.post("/submit-quiz", Authorization, userLimiter, ipLimiter, (req, res) => // answers = [{ questionId: 1, answer: "4" }, { questionId: 2, answer: "Paris" }] let userId = req.userId; + let ip = req.ip; let points = 0; answers.forEach(ans => { @@ -43,6 +46,7 @@ router.post("/submit-quiz", Authorization, userLimiter, ipLimiter, (req, res) => // If no rows updated, user not found if (result.changes === 0) { + logEvent("quiz_submission_failed_user_not_found", userId, getClientIp(ip)); return res.status(404).json({ error: "User not found" }); } @@ -51,11 +55,12 @@ router.post("/submit-quiz", Authorization, userLimiter, ipLimiter, (req, res) => // Broadcast live update broadcastProblem6({ type: 'leaderboardUpdate', leaderboard: users }); - + logEvent("quiz_submission_success", userId, getClientIp(ip)); res.json({ success: true, pointsEarned: points, leaderboard: users }); + } catch (err) { console.error(err); - broadcastProblem6({ type: 'error', message: 'Quiz submission failed' }); + logEvent("quiz_submission_error", req.userId, getClientIp(ip)); res.status(500).json({ error: "Failed to submit quiz" }); } }); From edf8af835ebfcb66330cef480329241cc04b1b90 Mon Sep 17 00:00:00 2001 From: agaes Date: Fri, 13 Feb 2026 12:57:53 +0800 Subject: [PATCH 24/26] feat(problem6): add input sanitization with Joi - Implemented QuizValidator.js using Joi to enforce strict schema validation - Ensures answers array contains valid questionId (integer) and answer (string) in QuizValidator - Integrated validation into submit-quiz route before processing logic - Strengthened input sanitization as a security measure for Problem 6 --- package-lock.json | 68 +++++++++++++++++++++++++++++++- package.json | 1 + src/middlewares/QuizValidator.js | 12 ++++++ src/problem6/Architecture.js | 15 +++++-- 4 files changed, 91 insertions(+), 5 deletions(-) create mode 100644 src/middlewares/QuizValidator.js diff --git a/package-lock.json b/package-lock.json index 20a449855..d391901d8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "express-rate-limit": "^8.2.1", "express-router": "^0.0.1", "helmet": "^8.1.0", + "joi": "^18.0.2", "jsonwebtoken": "^9.0.3", "ws": "^8.19.0" }, @@ -467,6 +468,54 @@ "node": ">=18" } }, + "node_modules/@hapi/address": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@hapi/address/-/address-5.1.1.tgz", + "integrity": "sha512-A+po2d/dVoY7cYajycYI43ZbYMXukuopIsqCjh5QzsBCipDtdofHntljDlpccMjIfTy6UOkg+5KPriwYch2bXA==", + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^11.0.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@hapi/formula": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@hapi/formula/-/formula-3.0.2.tgz", + "integrity": "sha512-hY5YPNXzw1He7s0iqkRQi+uMGh383CGdyyIGYtB+W5N3KHPXoqychklvHhKCC9M3Xtv0OCs/IHw+r4dcHtBYWw==", + "license": "BSD-3-Clause" + }, + "node_modules/@hapi/hoek": { + "version": "11.0.7", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-11.0.7.tgz", + "integrity": "sha512-HV5undWkKzcB4RZUusqOpcgxOaq6VOAH7zhhIr2g3G8NF/MlFO75SjOr2NfuSx0Mh40+1FqCkagKLJRykUWoFQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@hapi/pinpoint": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@hapi/pinpoint/-/pinpoint-2.0.1.tgz", + "integrity": "sha512-EKQmr16tM8s16vTT3cA5L0kZZcTMU5DUOZTuvpnY738m+jyP3JIUj+Mm1xc1rsLkGBQ/gVnfKYPwOmPg1tUR4Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@hapi/tlds": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@hapi/tlds/-/tlds-1.1.5.tgz", + "integrity": "sha512-Vq/1gnIIsvFUpKlDdfrPd/ssHDpAyBP/baVukh3u2KSG2xoNjsnRNjQiPmuyPPGqsn1cqVWWhtZHfOBaLizFRQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@hapi/topo": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-6.0.2.tgz", + "integrity": "sha512-KR3rD5inZbGMrHmgPxsJ9dbi6zEK+C3ZwUwTa+eMwWLz7oijWUTWD2pMSNNYJAU6Qq+65NkxXjqHr/7LM2Xkqg==", + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^11.0.2" + } + }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", @@ -851,7 +900,6 @@ "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": { @@ -2296,6 +2344,24 @@ "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", "license": "MIT" }, + "node_modules/joi": { + "version": "18.0.2", + "resolved": "https://registry.npmjs.org/joi/-/joi-18.0.2.tgz", + "integrity": "sha512-RuCOQMIt78LWnktPoeBL0GErkNaJPTBGcYuyaBvUOQSpcpcLfWrHPPihYdOGbV5pam9VTWbeoF7TsGiHugcjGA==", + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/address": "^5.1.1", + "@hapi/formula": "^3.0.2", + "@hapi/hoek": "^11.0.7", + "@hapi/pinpoint": "^2.0.1", + "@hapi/tlds": "^1.1.1", + "@hapi/topo": "^6.0.2", + "@standard-schema/spec": "^1.0.0" + }, + "engines": { + "node": ">= 20" + } + }, "node_modules/jsonwebtoken": { "version": "9.0.3", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", diff --git a/package.json b/package.json index 6f09c505d..2b254708f 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "express-rate-limit": "^8.2.1", "express-router": "^0.0.1", "helmet": "^8.1.0", + "joi": "^18.0.2", "jsonwebtoken": "^9.0.3", "ws": "^8.19.0" }, diff --git a/src/middlewares/QuizValidator.js b/src/middlewares/QuizValidator.js new file mode 100644 index 000000000..bd409b817 --- /dev/null +++ b/src/middlewares/QuizValidator.js @@ -0,0 +1,12 @@ +import Joi from "joi"; + +const quizSchema = Joi.object({ + answers: Joi.array().items( + Joi.object({ + questionId: Joi.number().integer().required(), + answer: Joi.string().trim().required() + }) + ).min(1).required() +}); + +export default quizSchema; \ No newline at end of file diff --git a/src/problem6/Architecture.js b/src/problem6/Architecture.js index 26b1a3358..d788f1ade 100644 --- a/src/problem6/Architecture.js +++ b/src/problem6/Architecture.js @@ -6,7 +6,7 @@ import Authorization from '../middlewares/authorization.js'; import { userLimiter, ipLimiter } from '../middlewares/ratelimiter.js'; import logEvent from '../middlewares/auditLogger.js'; import getClientIp from '../middlewares/getClientIp.js'; - +import quizSchema from '../middlewares/QuizValidator.js'; const router = express.Router(); router.get('/leaderboard', (req, res) => { @@ -27,11 +27,18 @@ router.get('/leaderboard', (req, res) => { router.post("/submit-quiz", Authorization, userLimiter, ipLimiter, (req, res) => { try { - const { answers } = req.body; - // answers = [{ questionId: 1, answer: "4" }, { questionId: 2, answer: "Paris" }] + const { error, value } = quizSchema.validate(req.body, { abortEarly: false }); + let ip = req.ip; + + if (error) { + logEvent("quiz_submission_invalid_input", req.userId, getClientIp(ip)); + return res.status(400).json({ error: error.details.map(d => d.message) }); + } + const { answers } = value; + // answers = [{ questionId: 1, answer: "4" }, { questionId: 2, answer: "Paris" }] let userId = req.userId; - let ip = req.ip; + let points = 0; answers.forEach(ans => { From 556ac887fa1d3cd429f7d147fd3af8966e3bf87f Mon Sep 17 00:00:00 2001 From: agaes Date: Fri, 13 Feb 2026 13:02:37 +0800 Subject: [PATCH 25/26] Completed Assessment Updated the readme.md for security enhancements. --- readme.md | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 58 insertions(+), 3 deletions(-) diff --git a/readme.md b/readme.md index d58416d9f..d740ca410 100644 --- a/readme.md +++ b/readme.md @@ -419,9 +419,64 @@ simple way to describe and set up JWT Bearer authorization in Postman: --- -## 🔜 Next Steps -- Add **audit logging** for score updates. -- Extend Client.js to display leaderboard visually in the browser. +Here’s a **README section** you can add to your project that documents the security enhancements you’ve implemented: --- +# 🛡️ Security Enhancements + +This project includes multiple layers of security to protect API endpoints, user data, and application integrity. + +## Features Implemented + +### 1. **Audit Logging** +- Added `auditLogger.js` to record all critical events into a log file. +- Logs include: + - Authorization attempts (success/failure). + - Quiz submission attempts, successes, and errors. + - Invalid input submissions. +- Each log entry captures **event type, userId, IP address (normalized to IPv4), and timestamp**. + +### 2. **Authorization (JWT)** +- Middleware verifies JWT tokens before granting access to protected routes. +- Attaches `userId` to the request object for downstream use. +- Unauthorized or invalid tokens are logged and blocked. + +### 3. **Rate Limiting** +- Implemented **per-user** and **per-IP** rate limiting. +- Prevents brute-force attacks and abuse of endpoints. +- Logs excessive request attempts for monitoring. + +### 4. **Helmet Integration** +- Uses [Helmet](https://helmetjs.github.io/) to set secure HTTP headers. +- Protects against common web vulnerabilities such as: + - Cross-Site Scripting (XSS). + - Clickjacking. + - MIME sniffing. + +### 5. **Input Validation (Joi)** +- All incoming payloads are validated using **Joi schemas**. +- Example: `answers` array in `/submit-quiz` must contain valid `questionId` (integer) and `answer` (string). +- Invalid inputs are rejected with clear error messages and logged for audit. + +### 6. **IP Normalization** +- Added `getClientIp.js` helper to ensure consistent IPv4 logging. +- Converts `::1` (IPv6 localhost) to `127.0.0.1`. +- Strips IPv6 prefixes like `::ffff:` to log clean IPv4 addresses. + +--- + +## Benefits +- **Defense in depth**: multiple layers (auth, rate limiting, validation, headers). +- **Visibility**: audit logs provide a clear trail of all access attempts and actions. +- **Resilience**: prevents malformed input, brute-force attacks, and common exploits. +- **Maintainability**: reusable helpers and middleware keep security consistent across routes. + +--- + +✅ With these enhancements, the application is significantly hardened against abuse, unauthorized access, and common malware vectors. + +## 🔜 Improvement +- Extend Client.js to display leaderboard visually in the browser required frontend implementation. +--- + From d20cf3af6daae4cb804d21903c7477b8d1c9bc05 Mon Sep 17 00:00:00 2001 From: agaes Date: Fri, 13 Feb 2026 13:59:47 +0800 Subject: [PATCH 26/26] updated diagram format --- readme.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/readme.md b/readme.md index d740ca410..b77c27ad7 100644 --- a/readme.md +++ b/readme.md @@ -374,10 +374,11 @@ simple way to describe and set up JWT Bearer authorization in Postman: ## Visual Diagram that Illustrate Flow Of Execution. +```bash +------------------+ | | | Client UI | -| (Browser/CLI) | +| (Browser / CLI) | | | +--------+---------+ | @@ -416,10 +417,7 @@ simple way to describe and set up JWT Bearer authorization in Postman: | (Client.js listening) | | | +---------------------------+ - ---- - -Here’s a **README section** you can add to your project that documents the security enhancements you’ve implemented: +``` ---