From 6d17cdacc14e38b42474085102de540a67b01cf3 Mon Sep 17 00:00:00 2001 From: Aarish <118203269+ImprobableGenius@users.noreply.github.com> Date: Thu, 22 May 2025 22:50:05 -0500 Subject: [PATCH 01/22] axe test helper - to reuse accessibility test fixures. If future these can be test options/types --- helpers/axe-test.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 helpers/axe-test.js diff --git a/helpers/axe-test.js b/helpers/axe-test.js new file mode 100644 index 0000000..a2136ee --- /dev/null +++ b/helpers/axe-test.js @@ -0,0 +1,17 @@ +const base = require('@playwright/test'); +const AxeBuilder = require('@axe-core/playwright').default; + +// Extend base test by providing "makeAxeBuilder" +// +// This new "test" can be used in multiple test files, and each of them will get +// a consistently configured AxeBuilder instance. +exports.test = base.test.extend({ + makeAxeBuilder: async ({ page }, use) => { + const makeAxeBuilder = () => new AxeBuilder({ page }) + .withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa']) + .exclude('#commonly-reused-element-with-known-issue'); + + await use(makeAxeBuilder); + } +}); +exports.expect = base.expect; \ No newline at end of file From a624b4c8a223e8226eb4cf0d1b5887f658aeaae5 Mon Sep 17 00:00:00 2001 From: Aarish <118203269+ImprobableGenius@users.noreply.github.com> Date: Thu, 22 May 2025 22:50:19 -0500 Subject: [PATCH 02/22] build api endpoints /api --- routes/api.js | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 routes/api.js diff --git a/routes/api.js b/routes/api.js new file mode 100644 index 0000000..b5dcd12 --- /dev/null +++ b/routes/api.js @@ -0,0 +1,26 @@ +// import router from express +var express = require('express'); +const PlaywrightService = require('../services/PlaywrightService'); +var router = express.Router(); + +/** + * GET run accessibility test on domain + * Only works for homepage + * // TODO: Crawl site map and queue all pages + */ +router.get('/test/accessibility/:domain', function(req, res) { + const domain = req.params.domain; // Get the domain from the request parameters + + const playwrightService = new PlaywrightService(domain); + + // Call the runAccessibilityTest method + playwrightService.runAccessibilityTest().then(results => { + // Send the results as a JSON response + res.json(results); + }).catch(err => { + // Handle any errors that occur during the test + res.status(500).json({ error: err.message }); + }); +}); + +module.exports = router; \ No newline at end of file From 1f77198683a4e8b30fc174f96a0607f5bf927c62 Mon Sep 17 00:00:00 2001 From: Aarish <118203269+ImprobableGenius@users.noreply.github.com> Date: Thu, 22 May 2025 22:50:28 -0500 Subject: [PATCH 03/22] register /api endpoints --- app.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app.js b/app.js index 662bcc9..66f0060 100644 --- a/app.js +++ b/app.js @@ -6,6 +6,7 @@ var logger = require('morgan'); var indexRouter = require('./routes/index'); var usersRouter = require('./routes/users'); +const apiRouter = require('./routes/api'); var app = express(); @@ -20,6 +21,8 @@ app.use(cookieParser()); app.use(express.static(path.join(__dirname, 'public'))); app.use('/', indexRouter); + +app.use('/api', apiRouter); app.use('/users', usersRouter); // catch 404 and forward to error handler From 67b91d96244311da1d3ef834f609e5e633ed6298 Mon Sep 17 00:00:00 2001 From: Aarish <118203269+ImprobableGenius@users.noreply.github.com> Date: Thu, 22 May 2025 22:51:11 -0500 Subject: [PATCH 04/22] Build a service class to handle Playwright Testing --- services/PlaywrightService.js | 52 +++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 services/PlaywrightService.js diff --git a/services/PlaywrightService.js b/services/PlaywrightService.js new file mode 100644 index 0000000..09d8cb6 --- /dev/null +++ b/services/PlaywrightService.js @@ -0,0 +1,52 @@ +// import playwright dependencies +const { test, expect } = require('../helpers/axe-test.js'); +const { chromium } = require('playwright'); +const { injectAxe, checkA11y } = require('axe-playwright'); + + + +class PlaywrightService { + + #domain; // domain of the site to be tested + #allyTest; // ally test object + #url; + + + // constructor + constructor(domain) { + this.#domain = domain; + this.#url = `https://${domain}`; + } + + /** + * + */ + async runAccessibilityTest() { + + const browser = await chromium.launch(); + const page = await browser.newPage(); + + try { + await page.goto(this.#url); + await injectAxe(page); + const results = await page.evaluate(async () => { + return await window.axe.run(); + }); + + return results.violations; + } finally { + await browser.close(); + } + + + + return results; + } + + /** + * + */ + +} + +module.exports = PlaywrightService; \ No newline at end of file From 79c4ecdc7a38382c2ff41853bc52efc50caa8d94 Mon Sep 17 00:00:00 2001 From: Aarish <118203269+ImprobableGenius@users.noreply.github.com> Date: Thu, 22 May 2025 22:51:26 -0500 Subject: [PATCH 05/22] Include playright and axe-core dependencies --- package-lock.json | 173 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 3 + 2 files changed, 176 insertions(+) diff --git a/package-lock.json b/package-lock.json index 31c6822..6030c29 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,14 +8,48 @@ "name": "playwright-a11y-dashboard", "version": "0.0.0", "dependencies": { + "@axe-core/playwright": "^4.10.1", + "@playwright/test": "^1.52.0", + "axe-playwright": "^2.1.0", "cookie-parser": "~1.4.4", "debug": "~2.6.9", "express": "~4.16.1", "http-errors": "~1.6.3", "jade": "~1.11.0", "morgan": "~1.9.1" + }, + "devDependencies": {} + }, + "node_modules/@axe-core/playwright": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/@axe-core/playwright/-/playwright-4.10.1.tgz", + "integrity": "sha512-EV5t39VV68kuAfMKqb/RL+YjYKhfuGim9rgIaQ6Vntb2HgaCaau0h98Y3WEUqW1+PbdzxDtDNjFAipbtZuBmEA==", + "dependencies": { + "axe-core": "~4.10.2" + }, + "peerDependencies": { + "playwright-core": ">= 1.0.0" } }, + "node_modules/@playwright/test": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.52.0.tgz", + "integrity": "sha512-uh6W7sb55hl7D6vsAeA+V2p5JnlAqzhqFyF0VcJkKZXkgnFcVG9PziERRHQfPLfNGx1C292a4JqbWzhR8L4R1g==", + "dependencies": { + "playwright": "1.52.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@types/junit-report-builder": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/junit-report-builder/-/junit-report-builder-3.0.2.tgz", + "integrity": "sha512-R5M+SYhMbwBeQcNXYWNCZkl09vkVfAtcPIaCGdzIkkbeaTrVbGQ7HVgi4s+EmM/M1K4ZuWQH0jGcvMvNePfxYA==" + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -78,6 +112,43 @@ "resolved": "https://registry.npmjs.org/asap/-/asap-1.0.0.tgz", "integrity": "sha512-Ej9qjcXY+8Tuy1cNqiwNMwFRXOy9UwgTeMA8LxreodygIPV48lx8PU1ecFxb5ZeU1DpMKxiq6vGLTxcitWZPbA==" }, + "node_modules/axe-core": { + "version": "4.10.3", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.3.tgz", + "integrity": "sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg==", + "engines": { + "node": ">=4" + } + }, + "node_modules/axe-html-reporter": { + "version": "2.2.11", + "resolved": "https://registry.npmjs.org/axe-html-reporter/-/axe-html-reporter-2.2.11.tgz", + "integrity": "sha512-WlF+xlNVgNVWiM6IdVrsh+N0Cw7qupe5HT9N6Uyi+aN7f6SSi92RDomiP1noW8OWIV85V6x404m5oKMeqRV3tQ==", + "dependencies": { + "mustache": "^4.0.1" + }, + "engines": { + "node": ">=8.9.0" + }, + "peerDependencies": { + "axe-core": ">=3" + } + }, + "node_modules/axe-playwright": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/axe-playwright/-/axe-playwright-2.1.0.tgz", + "integrity": "sha512-tY48SX56XaAp16oHPyD4DXpybz8Jxdz9P7exTjF/4AV70EGUavk+1fUPWirM0OYBR+YyDx6hUeDvuHVA6fB9YA==", + "dependencies": { + "@types/junit-report-builder": "^3.0.2", + "axe-core": "^4.10.1", + "axe-html-reporter": "2.2.11", + "junit-report-builder": "^5.1.1", + "picocolors": "^1.1.1" + }, + "peerDependencies": { + "playwright": ">1.0.0" + } + }, "node_modules/basic-auth": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", @@ -391,6 +462,19 @@ "node": ">= 0.6" } }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/graceful-readlink": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", @@ -474,6 +558,19 @@ "promise": "^6.0.1" } }, + "node_modules/junit-report-builder": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/junit-report-builder/-/junit-report-builder-5.1.1.tgz", + "integrity": "sha512-ZNOIIGMzqCGcHQEA2Q4rIQQ3Df6gSIfne+X9Rly9Bc2y55KxAZu8iGv+n2pP0bLf0XAOctJZgeloC54hWzCahQ==", + "dependencies": { + "lodash": "^4.17.21", + "make-dir": "^3.1.0", + "xmlbuilder": "^15.1.1" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", @@ -493,6 +590,11 @@ "node": ">=0.10.0" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, "node_modules/longest": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", @@ -501,6 +603,20 @@ "node": ">=0.10.0" } }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -588,6 +704,14 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/mustache": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", + "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", + "bin": { + "mustache": "bin/mustache" + } + }, "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -636,6 +760,39 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" + }, + "node_modules/playwright": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.52.0.tgz", + "integrity": "sha512-JAwMNMBlxJ2oD1kce4KPtMkDeKGHQstdpFPcPH3maElAXon/QZeTvtsfXmTMRyO9TslfoYOXkSsvao2nE1ilTw==", + "dependencies": { + "playwright-core": "1.52.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.52.0.tgz", + "integrity": "sha512-l2osTgLXSMeuLZOML9qYODUQoPPnUsKsb5/P6LJ2e6uPKXUdPK5WYhN4z03G+YNbWmGDY4YENauNu4ZKczreHg==", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/promise": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/promise/-/promise-6.1.0.tgz", @@ -715,6 +872,14 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/send": { "version": "0.16.2", "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", @@ -938,6 +1103,14 @@ "node": ">=0.4.0" } }, + "node_modules/xmlbuilder": { + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", + "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", + "engines": { + "node": ">=8.0" + } + }, "node_modules/yargs": { "version": "3.10.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", diff --git a/package.json b/package.json index ffb30f0..c199941 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,9 @@ "start": "node ./bin/www" }, "dependencies": { + "@axe-core/playwright": "^4.10.1", + "@playwright/test": "^1.52.0", + "axe-playwright": "^2.1.0", "cookie-parser": "~1.4.4", "debug": "~2.6.9", "express": "~4.16.1", From eece05bced8df3287e8c9b2f3a6c0ea14b5cb2d7 Mon Sep 17 00:00:00 2001 From: Aarish <118203269+ImprobableGenius@users.noreply.github.com> Date: Thu, 22 May 2025 23:45:24 -0500 Subject: [PATCH 06/22] remove unwanted instance variables --- services/PlaywrightService.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/services/PlaywrightService.js b/services/PlaywrightService.js index 09d8cb6..55f030f 100644 --- a/services/PlaywrightService.js +++ b/services/PlaywrightService.js @@ -8,9 +8,6 @@ const { injectAxe, checkA11y } = require('axe-playwright'); class PlaywrightService { #domain; // domain of the site to be tested - #allyTest; // ally test object - #url; - // constructor constructor(domain) { From 38da11fd996f7aebd322424bfbff9fe0a741460a Mon Sep 17 00:00:00 2001 From: Aarish <118203269+ImprobableGenius@users.noreply.github.com> Date: Thu, 22 May 2025 23:45:50 -0500 Subject: [PATCH 07/22] build methods to return site map url and site root url --- services/PlaywrightService.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/services/PlaywrightService.js b/services/PlaywrightService.js index 55f030f..25c17d6 100644 --- a/services/PlaywrightService.js +++ b/services/PlaywrightService.js @@ -12,7 +12,16 @@ class PlaywrightService { // constructor constructor(domain) { this.#domain = domain; - this.#url = `https://${domain}`; + } + + // get sitemap url for the this domain + sitemapUrl() { + return `${this.rootUrl()}/sitemap.xml`; + } + + // get the url of the site to be tested + rootUrl() { + return `https://${domain}`; } /** From 966b3f1c1f9b2d414242fc1b041d356c39e33488 Mon Sep 17 00:00:00 2001 From: Aarish <118203269+ImprobableGenius@users.noreply.github.com> Date: Thu, 22 May 2025 23:46:11 -0500 Subject: [PATCH 08/22] Add block comment for runAccessibilityTest method --- services/PlaywrightService.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/services/PlaywrightService.js b/services/PlaywrightService.js index 25c17d6..e485787 100644 --- a/services/PlaywrightService.js +++ b/services/PlaywrightService.js @@ -25,7 +25,12 @@ class PlaywrightService { } /** + * Get the list of urls to be tested by querying + * the sitemap and returning the list of urls * + * @param {string} url - The URL of the sitemap + * + * @returns {Array} - The list of urls to be tested */ async runAccessibilityTest() { From 4ccbef77d28b52c750916bd982f184cefc3025dc Mon Sep 17 00:00:00 2001 From: Aarish <118203269+ImprobableGenius@users.noreply.github.com> Date: Thu, 22 May 2025 23:46:37 -0500 Subject: [PATCH 09/22] refactor to use rootUrl method --- services/PlaywrightService.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/PlaywrightService.js b/services/PlaywrightService.js index e485787..8179046 100644 --- a/services/PlaywrightService.js +++ b/services/PlaywrightService.js @@ -38,7 +38,7 @@ class PlaywrightService { const page = await browser.newPage(); try { - await page.goto(this.#url); + await page.goto(this.rootUrl()); await injectAxe(page); const results = await page.evaluate(async () => { return await window.axe.run(); From 365c599cd3b3154ca0b3635e1f2af146a4d801ad Mon Sep 17 00:00:00 2001 From: Aarish <118203269+ImprobableGenius@users.noreply.github.com> Date: Fri, 23 May 2025 00:41:49 -0500 Subject: [PATCH 10/22] Include JSDOM dependency - using it to parse page.content returned by playwright --- package-lock.json | 471 +++++++++++++++++++++++++++++++++++++++++++++- package.json | 1 + 2 files changed, 470 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6030c29..abb3a83 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,9 +16,21 @@ "express": "~4.16.1", "http-errors": "~1.6.3", "jade": "~1.11.0", + "jsdom": "^26.1.0", "morgan": "~1.9.1" - }, - "devDependencies": {} + } + }, + "node_modules/@asamuzakjp/css-color": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.2.0.tgz", + "integrity": "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==", + "dependencies": { + "@csstools/css-calc": "^2.1.3", + "@csstools/css-color-parser": "^3.0.9", + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3", + "lru-cache": "^10.4.3" + } }, "node_modules/@axe-core/playwright": { "version": "4.10.1", @@ -31,6 +43,111 @@ "playwright-core": ">= 1.0.0" } }, + "node_modules/@csstools/color-helpers": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.0.2.tgz", + "integrity": "sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@csstools/css-calc": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.3.tgz", + "integrity": "sha512-XBG3talrhid44BY1x3MHzUx/aTG8+x/Zi57M4aTKK9RFB4aLlF3TTSzfzn8nWVHWL3FgAXAxmupmDd6VWww+pw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.0.9.tgz", + "integrity": "sha512-wILs5Zk7BU86UArYBJTPy/FMPPKVKHMj1ycCEyf3VUptol0JNRLFU/BZsJ4aiIHJEbSLiizzRrw8Pc1uAEDrXw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "dependencies": { + "@csstools/color-helpers": "^5.0.2", + "@csstools/css-calc": "^2.1.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.4.tgz", + "integrity": "sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^3.0.3" + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.3.tgz", + "integrity": "sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@playwright/test": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.52.0.tgz", @@ -81,6 +198,14 @@ "acorn": "^2.1.0" } }, + "node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "engines": { + "node": ">= 14" + } + }, "node_modules/align-text": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", @@ -326,6 +451,30 @@ "resolved": "https://registry.npmjs.org/css-stringify/-/css-stringify-1.0.5.tgz", "integrity": "sha512-aIThpcErhG5EyHorGqNlTh0TduNBqLrrXLO3x5rku3ZKBxuVfY+T7noyM2G2X/01iQANqJUb6d3+FLoa+N7Xwg==" }, + "node_modules/cssstyle": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.3.1.tgz", + "integrity": "sha512-ZgW+Jgdd7i52AaLYCriF8Mxqft0gD/R9i9wi6RWBhs1pqdPEzPjym7rvRKi397WmQFf3SlyUsszhw+VVCbx79Q==", + "dependencies": { + "@asamuzakjp/css-color": "^3.1.2", + "rrweb-cssom": "^0.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/data-urls": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", + "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", + "dependencies": { + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -342,6 +491,11 @@ "node": ">=0.10.0" } }, + "node_modules/decimal.js": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.5.0.tgz", + "integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==" + }, "node_modules/depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -368,6 +522,17 @@ "node": ">= 0.8" } }, + "node_modules/entities": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.0.tgz", + "integrity": "sha512-aKstq2TDOndCn4diEyp9Uq/Flu2i1GlLkc6XIDQSDMuaFE3OPW5OphLCyQ5SpSJZTb4reN+kTcYru5yIfXoRPw==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -480,6 +645,17 @@ "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", "integrity": "sha512-8tLu60LgxF6XpdbK8OW3FA+IfTNBn1ZHGHKF4KQbEeSkajYw5PlYJcKluntgegDPTg8UkHjpet1T82vk6TQ68w==" }, + "node_modules/html-encoding-sniffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", + "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", + "dependencies": { + "whatwg-encoding": "^3.1.1" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/http-errors": { "version": "1.6.3", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", @@ -494,6 +670,72 @@ "node": ">= 0.6" } }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/http-proxy-agent/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/http-proxy-agent/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==" + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/https-proxy-agent/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==" + }, "node_modules/iconv-lite": { "version": "0.4.23", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", @@ -523,6 +765,11 @@ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==" + }, "node_modules/is-promise": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", @@ -549,6 +796,44 @@ "jade": "bin/jade.js" } }, + "node_modules/jsdom": { + "version": "26.1.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.1.0.tgz", + "integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==", + "dependencies": { + "cssstyle": "^4.2.1", + "data-urls": "^5.0.0", + "decimal.js": "^10.5.0", + "html-encoding-sniffer": "^4.0.0", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.6", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.16", + "parse5": "^7.2.1", + "rrweb-cssom": "^0.8.0", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^5.1.1", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^3.1.1", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.1.1", + "ws": "^8.18.0", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "canvas": "^3.0.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, "node_modules/jstransformer": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-0.0.2.tgz", @@ -603,6 +888,11 @@ "node": ">=0.10.0" } }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==" + }, "node_modules/make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", @@ -720,6 +1010,11 @@ "node": ">= 0.6" } }, + "node_modules/nwsapi": { + "version": "2.2.20", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.20.tgz", + "integrity": "sha512-/ieB+mDe4MrrKMT8z+mQL8klXydZWGR5Dowt4RAGKbJ3kIGEx3X4ljUo+6V73IXtUPWgfOlU5B9MlGxFO5T+cA==" + }, "node_modules/on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", @@ -747,6 +1042,17 @@ "wordwrap": "~0.0.2" } }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -813,6 +1119,14 @@ "node": ">= 0.10" } }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "engines": { + "node": ">=6" + } + }, "node_modules/qs": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", @@ -862,6 +1176,11 @@ "node": ">=0.10.0" } }, + "node_modules/rrweb-cssom": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", + "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==" + }, "node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -872,6 +1191,17 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, "node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -941,6 +1271,49 @@ "node": ">= 0.6" } }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" + }, + "node_modules/tldts": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz", + "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==", + "dependencies": { + "tldts-core": "^6.1.86" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz", + "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==" + }, + "node_modules/tough-cookie": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", + "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", + "dependencies": { + "tldts": "^6.1.32" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tr46": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/transformers": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/transformers/-/transformers-2.1.0.tgz", @@ -1067,6 +1440,67 @@ "node": ">=0.10.0" } }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-url": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", + "dependencies": { + "tr46": "^5.1.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/window-size": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", @@ -1103,6 +1537,34 @@ "node": ">=0.4.0" } }, + "node_modules/ws": { + "version": "8.18.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.2.tgz", + "integrity": "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==", + "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 + } + } + }, + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "engines": { + "node": ">=18" + } + }, "node_modules/xmlbuilder": { "version": "15.1.1", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", @@ -1111,6 +1573,11 @@ "node": ">=8.0" } }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" + }, "node_modules/yargs": { "version": "3.10.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", diff --git a/package.json b/package.json index c199941..eab019a 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "express": "~4.16.1", "http-errors": "~1.6.3", "jade": "~1.11.0", + "jsdom": "^26.1.0", "morgan": "~1.9.1" } } From b194ff6924f0b7619c5640ba1d50796330150c51 Mon Sep 17 00:00:00 2001 From: Aarish <118203269+ImprobableGenius@users.noreply.github.com> Date: Fri, 23 May 2025 00:42:22 -0500 Subject: [PATCH 11/22] Update api route timeout, this is resource intensive task, find a better way to handle this --- routes/api.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/routes/api.js b/routes/api.js index b5dcd12..e630687 100644 --- a/routes/api.js +++ b/routes/api.js @@ -9,12 +9,13 @@ var router = express.Router(); * // TODO: Crawl site map and queue all pages */ router.get('/test/accessibility/:domain', function(req, res) { + req.setTimeout(5000000000000); const domain = req.params.domain; // Get the domain from the request parameters const playwrightService = new PlaywrightService(domain); // Call the runAccessibilityTest method - playwrightService.runAccessibilityTest().then(results => { + playwrightService.getAccessibilityResults().then(results => { // Send the results as a JSON response res.json(results); }).catch(err => { From 7773e858112a342c9b39b26d49ca39d297334f83 Mon Sep 17 00:00:00 2001 From: Aarish <118203269+ImprobableGenius@users.noreply.github.com> Date: Fri, 23 May 2025 00:42:55 -0500 Subject: [PATCH 12/22] refactor playwright service class --- services/PlaywrightService.js | 75 +++++++++++++++++++++++++++++------ 1 file changed, 62 insertions(+), 13 deletions(-) diff --git a/services/PlaywrightService.js b/services/PlaywrightService.js index 8179046..2ff900e 100644 --- a/services/PlaywrightService.js +++ b/services/PlaywrightService.js @@ -1,10 +1,14 @@ // import playwright dependencies -const { test, expect } = require('../helpers/axe-test.js'); const { chromium } = require('playwright'); const { injectAxe, checkA11y } = require('axe-playwright'); +const jsdom = require("jsdom"); - +/** + * PlaywrightService class + * + * This class is used to interact with the Playwright library + */ class PlaywrightService { #domain; // domain of the site to be tested @@ -21,7 +25,7 @@ class PlaywrightService { // get the url of the site to be tested rootUrl() { - return `https://${domain}`; + return `https://${this.#domain}`; } /** @@ -32,28 +36,73 @@ class PlaywrightService { * * @returns {Array} - The list of urls to be tested */ - async runAccessibilityTest() { - + async getUrlList() { const browser = await chromium.launch(); const page = await browser.newPage(); + let urls = []; try { - await page.goto(this.rootUrl()); - await injectAxe(page); - const results = await page.evaluate(async () => { - return await window.axe.run(); - }); + await page.goto(this.sitemapUrl()); + const content = await page.content(); + const dom = new jsdom.JSDOM(content); + const sitemapUrls = dom.window.document.querySelectorAll('a[href]'); + urls = Array.from(sitemapUrls).map(link => link.href); - return results.violations; + console.log('Sitemap URLs:', urls); + } catch (error) { + console.error('Error fetching sitemap:', error); } finally { await browser.close(); } - - + + return urls; + } + + /** + * + * @param {*} url + * @returns + */ + async getAccessibilityResults() { + const urls = await this.getUrlList(); + let results = []; + + while (urls.length > 0) { + const url = urls.pop(); + console.log('Running accessibility test on:', url); + const result = await PlaywrightService.#runAccessibilityTest(url); + results.push([url, result]); + } return results; } + /** + * Run accessibility test on given url + * + * @param {string} url - The URL of the page to test + * // TODO: Queue all urls from sitemap + */ + static async #runAccessibilityTest(url) { + const browser = await chromium.launch(); + const page = await browser.newPage(); + let results = []; + + try { + await page.goto(url); + await injectAxe(page); + results = await page.evaluate(async () => { + return await window.axe.run(); + }); + } catch(error) { + console.error('Error running accessibility test:', error); + } finally { + await browser.close(); + } + + return results?.violations || 'No violations found'; + } + /** * */ From 355837ffd0d9236c27043717e866f4d8d7388adc Mon Sep 17 00:00:00 2001 From: Aarish <118203269+ImprobableGenius@users.noreply.github.com> Date: Fri, 23 May 2025 00:48:47 -0500 Subject: [PATCH 13/22] Better comments --- services/PlaywrightService.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/services/PlaywrightService.js b/services/PlaywrightService.js index 2ff900e..b86ffd1 100644 --- a/services/PlaywrightService.js +++ b/services/PlaywrightService.js @@ -59,9 +59,10 @@ class PlaywrightService { } /** + * Loops through the list of urls and runs the + * accessibility test on each url * - * @param {*} url - * @returns + * @returns {Array} - The list of results from the accessibility test */ async getAccessibilityResults() { const urls = await this.getUrlList(); @@ -103,10 +104,6 @@ class PlaywrightService { return results?.violations || 'No violations found'; } - /** - * - */ - } module.exports = PlaywrightService; \ No newline at end of file From d79c581ea8dbd6d37c18c9228975c3a3728a8589 Mon Sep 17 00:00:00 2001 From: Aarish <118203269+ImprobableGenius@users.noreply.github.com> Date: Fri, 23 May 2025 00:55:04 -0500 Subject: [PATCH 14/22] Better comments --- services/PlaywrightService.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/services/PlaywrightService.js b/services/PlaywrightService.js index b86ffd1..d640d4b 100644 --- a/services/PlaywrightService.js +++ b/services/PlaywrightService.js @@ -82,7 +82,8 @@ class PlaywrightService { * Run accessibility test on given url * * @param {string} url - The URL of the page to test - * // TODO: Queue all urls from sitemap + * + * @returns {Array} - The list of results from the accessibility test */ static async #runAccessibilityTest(url) { const browser = await chromium.launch(); From a27ffb30163817777c09ff155d40f8485fa4096d Mon Sep 17 00:00:00 2001 From: Aarish <118203269+ImprobableGenius@users.noreply.github.com> Date: Fri, 23 May 2025 00:55:45 -0500 Subject: [PATCH 15/22] Add a todoComment --- services/PlaywrightService.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/PlaywrightService.js b/services/PlaywrightService.js index d640d4b..1082ba9 100644 --- a/services/PlaywrightService.js +++ b/services/PlaywrightService.js @@ -91,7 +91,7 @@ class PlaywrightService { let results = []; try { - await page.goto(url); + await page.goto(url); // TODO: Retry if this times out await injectAxe(page); results = await page.evaluate(async () => { return await window.axe.run(); From 369a2cc1b7bdffea464ca689361aaf436e0225cb Mon Sep 17 00:00:00 2001 From: Aarish <118203269+ImprobableGenius@users.noreply.github.com> Date: Fri, 23 May 2025 22:38:39 -0500 Subject: [PATCH 16/22] include client to interact with supabase api --- package-lock.json | 113 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 1 + 2 files changed, 114 insertions(+) diff --git a/package-lock.json b/package-lock.json index abb3a83..5fb2c82 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "@axe-core/playwright": "^4.10.1", "@playwright/test": "^1.52.0", + "@supabase/supabase-js": "^2.49.8", "axe-playwright": "^2.1.0", "cookie-parser": "~1.4.4", "debug": "~2.6.9", @@ -162,11 +163,118 @@ "node": ">=18" } }, + "node_modules/@supabase/auth-js": { + "version": "2.69.1", + "resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.69.1.tgz", + "integrity": "sha512-FILtt5WjCNzmReeRLq5wRs3iShwmnWgBvxHfqapC/VoljJl+W8hDAyFmf1NVw3zH+ZjZ05AKxiKxVeb0HNWRMQ==", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/functions-js": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.4.4.tgz", + "integrity": "sha512-WL2p6r4AXNGwop7iwvul2BvOtuJ1YQy8EbOd0dhG1oN1q8el/BIRSFCFnWAMM/vJJlHWLi4ad22sKbKr9mvjoA==", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/node-fetch": { + "version": "2.6.15", + "resolved": "https://registry.npmjs.org/@supabase/node-fetch/-/node-fetch-2.6.15.tgz", + "integrity": "sha512-1ibVeYUacxWYi9i0cf5efil6adJ9WRyZBLivgjs+AUpewx1F3xPi7gLgaASI2SmIQxPoCEjAsLAzKPgMJVgOUQ==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + } + }, + "node_modules/@supabase/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/@supabase/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/@supabase/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/@supabase/postgrest-js": { + "version": "1.19.4", + "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-1.19.4.tgz", + "integrity": "sha512-O4soKqKtZIW3olqmbXXbKugUtByD2jPa8kL2m2c1oozAO11uCcGrRhkZL0kVxjBLrXHE0mdSkFsMj7jDSfyNpw==", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/realtime-js": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.11.2.tgz", + "integrity": "sha512-u/XeuL2Y0QEhXSoIPZZwR6wMXgB+RQbJzG9VErA3VghVt7uRfSVsjeqd7m5GhX3JR6dM/WRmLbVR8URpDWG4+w==", + "dependencies": { + "@supabase/node-fetch": "^2.6.14", + "@types/phoenix": "^1.5.4", + "@types/ws": "^8.5.10", + "ws": "^8.18.0" + } + }, + "node_modules/@supabase/storage-js": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.7.1.tgz", + "integrity": "sha512-asYHcyDR1fKqrMpytAS1zjyEfvxuOIp1CIXX7ji4lHHcJKqyk+sLl/Vxgm4sN6u8zvuUtae9e4kDxQP2qrwWBA==", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/supabase-js": { + "version": "2.49.8", + "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.49.8.tgz", + "integrity": "sha512-zzBQLgS/jZs7btWcIAc7V5yfB+juG7h0AXxKowMJuySsO5vK+F7Vp+HCa07Z+tu9lZtr3sT9fofkc86bdylmtw==", + "dependencies": { + "@supabase/auth-js": "2.69.1", + "@supabase/functions-js": "2.4.4", + "@supabase/node-fetch": "2.6.15", + "@supabase/postgrest-js": "1.19.4", + "@supabase/realtime-js": "2.11.2", + "@supabase/storage-js": "2.7.1" + } + }, "node_modules/@types/junit-report-builder": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/@types/junit-report-builder/-/junit-report-builder-3.0.2.tgz", "integrity": "sha512-R5M+SYhMbwBeQcNXYWNCZkl09vkVfAtcPIaCGdzIkkbeaTrVbGQ7HVgi4s+EmM/M1K4ZuWQH0jGcvMvNePfxYA==" }, + "node_modules/@types/node": { + "version": "22.15.21", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.21.tgz", + "integrity": "sha512-EV/37Td6c+MgKAbkcLG6vqZ2zEYHD7bvSrzqqs2RIhbA6w3x+Dqz8MZM3sP6kGTeLrdoOgKZe+Xja7tUB2DNkQ==", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/phoenix": { + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.6.tgz", + "integrity": "sha512-PIzZZlEppgrpoT2QgbnDU+MMzuR6BbCjllj0bM70lWoejMeNJAxCchxnv7J3XFkI8MpygtRpzXrIlmWUBclP5A==" + }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -1408,6 +1516,11 @@ "integrity": "sha512-vb2s1lYx2xBtUgy+ta+b2J/GLVUR+wmpINwHePmPRhOsIVCG2wDzKJ0n14GslH1BifsqVzSOwQhRaCAsZ/nI4Q==", "optional": true }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==" + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", diff --git a/package.json b/package.json index eab019a..751b003 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "dependencies": { "@axe-core/playwright": "^4.10.1", "@playwright/test": "^1.52.0", + "@supabase/supabase-js": "^2.49.8", "axe-playwright": "^2.1.0", "cookie-parser": "~1.4.4", "debug": "~2.6.9", From fc360fd2ca7d2b2dfad9986022635ff9ddd27896 Mon Sep 17 00:00:00 2001 From: Aarish <118203269+ImprobableGenius@users.noreply.github.com> Date: Fri, 23 May 2025 22:38:59 -0500 Subject: [PATCH 17/22] Build a autheticaiton module for supabase --- auth.js | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 auth.js diff --git a/auth.js b/auth.js new file mode 100644 index 0000000..61a0439 --- /dev/null +++ b/auth.js @@ -0,0 +1,8 @@ +const { createClient } = require('@supabase/supabase-js'); + +const supabase = createClient( + 'https://rwulmnzzmieuosakijoe.supabase.co', + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InJ3dWxtbnp6bWlldW9zYWtpam9lIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NDgwNTQ4NDEsImV4cCI6MjA2MzYzMDg0MX0.ZEBJ6v0-u77z1cqRJvA8WOcGRxd8fcN0vqeYJNQgd_U' +); + +module.exports.supabase = supabase; \ No newline at end of file From a30942daae4dd4cb831799670394c1cbff6a60b2 Mon Sep 17 00:00:00 2001 From: Aarish <118203269+ImprobableGenius@users.noreply.github.com> Date: Fri, 23 May 2025 22:39:21 -0500 Subject: [PATCH 18/22] build a model to interact with sites table --- models/SiteModel.js | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 models/SiteModel.js diff --git a/models/SiteModel.js b/models/SiteModel.js new file mode 100644 index 0000000..f17fa67 --- /dev/null +++ b/models/SiteModel.js @@ -0,0 +1,34 @@ +/** + * Model for managing sites table in the database. + */ + +const { supabase } = require('../auth'); + +class SiteModel { + + static tableName = 'sites'; + + /** + * Inserts a new site into the database. + * + * @param {string} name - The name of the site. + * @param {string} domainName - The domain name of the site. + * + * @returns {Promise} - The result of the insert operation. + */ + async insertSite(name, domainName) { + const { error } = await supabase.from(siteModel.tableName).insert({ + name: name, + domain_name: domainName, + }); + + if (error) { + console.error('Error inserting site:', error); + throw error; + } + + return data; + } +} + +module.exports = SiteModel; \ No newline at end of file From a1387db6b1665cf1e42451bda3807cae2228a335 Mon Sep 17 00:00:00 2001 From: Aarish <118203269+ImprobableGenius@users.noreply.github.com> Date: Fri, 23 May 2025 23:35:12 -0500 Subject: [PATCH 19/22] add get methods - siteModel --- models/SiteModel.js | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/models/SiteModel.js b/models/SiteModel.js index f17fa67..b5894b4 100644 --- a/models/SiteModel.js +++ b/models/SiteModel.js @@ -16,7 +16,7 @@ class SiteModel { * * @returns {Promise} - The result of the insert operation. */ - async insertSite(name, domainName) { + async insert(name, domainName) { const { error } = await supabase.from(siteModel.tableName).insert({ name: name, domain_name: domainName, @@ -29,6 +29,41 @@ class SiteModel { return data; } + + /** + * Retrieves all sites from the database. + * + * @returns {Promise} - An array of site objects. + */ + async getAll() { + const { data, error } = await supabase.from(SiteModel.tableName).select('*'); + + if (error) { + console.error('Error fetching sites:', error); + throw error; + } + + return data; + } + + /** + * Retrieves a site by its ID. + * + * @param {number} id - The ID of the site. + * + * @returns {Promise} - The site object. + */ + async getById(id) { + const { data, error } = await supabase.from(SiteModel.tableName).select('*').eq('id', id).single(); + + if (error) { + console.error('Error fetching site by ID:', error); + throw error; + } + + return data; + } + } module.exports = SiteModel; \ No newline at end of file From c1eabb9dac2ada1ec55a3fe6fe39bf3e8bd71066 Mon Sep 17 00:00:00 2001 From: Aarish <118203269+ImprobableGenius@users.noreply.github.com> Date: Fri, 23 May 2025 23:50:27 -0500 Subject: [PATCH 20/22] Build sites resource routes --- routes/sites.js | 67 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 routes/sites.js diff --git a/routes/sites.js b/routes/sites.js new file mode 100644 index 0000000..62d3c69 --- /dev/null +++ b/routes/sites.js @@ -0,0 +1,67 @@ +const express = require('express'); +const router = express.Router(); +const SiteModel = require('../models/SiteModel'); +const siteModel = new SiteModel(); + +/** + * GET sites listing. + * + * Returns a list of all sites in JSON format. + */ +router.get('/', function(req, res, next) { + const sites = siteModel.getAllSites(); + + res.json(sites); +}); + +/** + * GET site by ID. + * + * Returns a specific site by its ID in JSON format. + */ +router.get('/:id', function(req, res, next) { + const siteId = req.params.id; + + const site = siteModel.getSiteById(siteId); + + if (!site) { + return res.status(404).send('Site not found'); + } else { + res.json(site); + } +}); + +/** + * POST create a new site. + * + * Creates a new site with the provided domain name. + * + * // TODO: Implement validation for domain name format + * // TODO: Implement error handling for duplicate domains + * // TODO: Ability to add additional site properties (e.g., name, description) + */ +router.post('/add/:domain', function(req, res, next) { + const domain = req.params.domain; + + const newSite = siteModel.createSite(domain); + + if (!newSite) { + return res.status(400).send('Error creating site'); + } else { + res.status(201).json(newSite); + } +}); + +/** + * PUT update an existing site. + * + * Updates an existing site with the provided ID and new data. + */ + +// TODO: Implement update functionality + +/** + * DELETE remove a site by ID. + */ + +// TODO: Implement delete functionality From 5e7f97f2fb7ba28b49c0d06ccd19e604b83b89c4 Mon Sep 17 00:00:00 2001 From: Aarish <118203269+ImprobableGenius@users.noreply.github.com> Date: Sat, 24 May 2025 00:05:24 -0500 Subject: [PATCH 21/22] add export and fix async functions --- routes/sites.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/routes/sites.js b/routes/sites.js index 62d3c69..25db6db 100644 --- a/routes/sites.js +++ b/routes/sites.js @@ -8,8 +8,8 @@ const siteModel = new SiteModel(); * * Returns a list of all sites in JSON format. */ -router.get('/', function(req, res, next) { - const sites = siteModel.getAllSites(); +router.get('/', async function(req, res, next) { + const sites = await siteModel.getAll(); res.json(sites); }); @@ -19,10 +19,10 @@ router.get('/', function(req, res, next) { * * Returns a specific site by its ID in JSON format. */ -router.get('/:id', function(req, res, next) { +router.get('/:id', async function(req, res, next) { const siteId = req.params.id; - const site = siteModel.getSiteById(siteId); + const site = await siteModel.getById(siteId); if (!site) { return res.status(404).send('Site not found'); @@ -43,7 +43,7 @@ router.get('/:id', function(req, res, next) { router.post('/add/:domain', function(req, res, next) { const domain = req.params.domain; - const newSite = siteModel.createSite(domain); + const newSite = siteModel.insert(domain); if (!newSite) { return res.status(400).send('Error creating site'); @@ -65,3 +65,6 @@ router.post('/add/:domain', function(req, res, next) { */ // TODO: Implement delete functionality + + +module.exports = router; \ No newline at end of file From 44a69a48a47785b73e0f38dc3296c7f3430875e4 Mon Sep 17 00:00:00 2001 From: Aarish <118203269+ImprobableGenius@users.noreply.github.com> Date: Sat, 24 May 2025 00:05:47 -0500 Subject: [PATCH 22/22] init site routes - to move this into api later --- app.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app.js b/app.js index 66f0060..7d2dddd 100644 --- a/app.js +++ b/app.js @@ -7,6 +7,7 @@ var logger = require('morgan'); var indexRouter = require('./routes/index'); var usersRouter = require('./routes/users'); const apiRouter = require('./routes/api'); +const sitesRouter = require('./routes/sites'); var app = express(); @@ -20,10 +21,13 @@ app.use(express.urlencoded({ extended: false })); app.use(cookieParser()); app.use(express.static(path.join(__dirname, 'public'))); +/** + * Register routers for different routes + */ app.use('/', indexRouter); - app.use('/api', apiRouter); app.use('/users', usersRouter); +app.use('/sites', sitesRouter); // catch 404 and forward to error handler app.use(function(req, res, next) {