From 0b44fbe0dbf192ce30bf96ccd812bd3b631bec82 Mon Sep 17 00:00:00 2001 From: Dina Berry Date: Thu, 13 Mar 2025 00:05:32 +0000 Subject: [PATCH 01/27] wip progress - tests run bug fail --- .devcontainer/devcontainer.json | 6 +- package-lock.json | 8760 +++++++++++++++++++++++------- package.json | 3 +- testrunner/README.md | 28 + testrunner/package.json | 13 + testrunner/test/01-spies.test.js | 26 + 6 files changed, 6919 insertions(+), 1917 deletions(-) create mode 100644 testrunner/README.md create mode 100644 testrunner/package.json create mode 100644 testrunner/test/01-spies.test.js diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index a0bfac37..df4881e9 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -3,7 +3,7 @@ { "name": "Node.js", // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile - "image": "mcr.microsoft.com/devcontainers/javascript-node:1-20-bullseye", + "image": "mcr.microsoft.com/devcontainers/javascript-node:1-22-bullseye", "features": { "ghcr.io/devcontainers-contrib/features/npm-package:1": {} }, @@ -21,7 +21,7 @@ "github.vscode-github-actions" ] } - } + }, // Features to add to the dev container. More info: https://containers.dev/features. // "features": {}, @@ -30,7 +30,7 @@ // "forwardPorts": [], // Use 'postCreateCommand' to run commands after the container is created. - // "postCreateCommand": "yarn install", + "postCreateCommand": "npm install -g typescript" // Configure tool-specific properties. // "customizations": {}, diff --git a/package-lock.json b/package-lock.json index 6b903f02..75866788 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,6 +4,17 @@ "requires": true, "packages": { "": { + "workspaces": [ + "node-dependencies", + "nodejs-debug", + "nodejs-files", + "nodejs-http", + "nodejs-intro", + "nodejs-intro-esm", + "nodejs-route", + "unit-testing", + "testrunner" + ], "dependencies": { "prettier": "^3.1.0" }, @@ -28,7 +39,6 @@ "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", "dev": true, - "peer": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/trace-mapping": "^0.3.9" @@ -37,47 +47,238 @@ "node": ">=6.0.0" } }, + "node_modules/@azure/abort-controller": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", + "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-auth": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.9.0.tgz", + "integrity": "sha512-FPwHpZywuyasDSLMqJ6fhbOK3TqUdviZNF8OqRGA4W5Ewib2lEEZ+pBsYcBa88B2NGO/SEnYPGhyBqNlE8ilSw==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-util": "^1.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-client": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.9.3.tgz", + "integrity": "sha512-/wGw8fJ4mdpJ1Cum7s1S+VQyXt1ihwKLzfabS1O/RDADnmzVc01dHn44qD0BvGH6KlZNzOMW95tEpKqhkCChPA==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.4.0", + "@azure/core-rest-pipeline": "^1.9.1", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.6.1", + "@azure/logger": "^1.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-rest-pipeline": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.19.1.tgz", + "integrity": "sha512-zHeoI3NCs53lLBbWNzQycjnYKsA1CVKlnzSNuSFcUDwBp8HHVObePxrM7HaX+Ha5Ks639H7chNC9HOaIhNS03w==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.8.0", + "@azure/core-tracing": "^1.0.1", + "@azure/core-util": "^1.11.0", + "@azure/logger": "^1.0.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-tracing": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.2.0.tgz", + "integrity": "sha512-UKTiEJPkWcESPYJz3X5uKRYyOcJD+4nYph+KpfdPRnQJVrZfk0KJgdnaAWKfhsBBtAf/D58Az4AvCJEmWgIBAg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-util": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.11.0.tgz", + "integrity": "sha512-DxOSLua+NdpWoSqULhjDyAZTXFdP/LKkqtYuxxz1SCN289zk3OG8UOpnCQAz/tygyACBtWp/BoO72ptK7msY8g==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/cosmos": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@azure/cosmos/-/cosmos-4.2.0.tgz", + "integrity": "sha512-acfAQTYLxgB/iZK7XvTVYe9NPk6DECEgcIXDQhyn7Uo4dGxeeW5D3YqLjLJrrzND5Iawer3eUQ5/iiLWvTGAxQ==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.7.1", + "@azure/core-rest-pipeline": "^1.15.1", + "@azure/core-tracing": "^1.1.1", + "@azure/core-util": "^1.8.1", + "fast-json-stable-stringify": "^2.1.0", + "jsbi": "^4.3.0", + "priorityqueuejs": "^2.0.0", + "semaphore": "^1.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/identity": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.8.0.tgz", + "integrity": "sha512-l9ALUGHtFB/JfsqmA+9iYAp2a+cCwdNO/cyIr2y7nJLJsz1aae6qVP8XxT7Kbudg0IQRSIMXj0+iivFdbD1xPA==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.9.0", + "@azure/core-client": "^1.9.2", + "@azure/core-rest-pipeline": "^1.17.0", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.11.0", + "@azure/logger": "^1.0.0", + "@azure/msal-browser": "^4.2.0", + "@azure/msal-node": "^3.2.3", + "events": "^3.0.0", + "jws": "^4.0.0", + "open": "^10.1.0", + "stoppable": "^1.1.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/logger": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.1.4.tgz", + "integrity": "sha512-4IXXzcCdLdlXuCG+8UKEwLA1T1NHqUfanhXYHiQTn+6sfWCZXduqbtXDGceg3Ce5QxTGo7EqmbV6Bi+aqKuClQ==", + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/msal-browser": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-4.7.0.tgz", + "integrity": "sha512-H4AIPhIQVe1qW4+BJaitqod6UGQiXE3juj7q2ZBsOPjuZicQaqcbnBp2gCroF/icS0+TJ9rGuyCBJbjlAqVOGA==", + "license": "MIT", + "dependencies": { + "@azure/msal-common": "15.2.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-common": { + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-15.2.1.tgz", + "integrity": "sha512-eZHtYE5OHDN0o2NahCENkczQ6ffGc0MoUSAI3hpwGpZBHJXaEQMMZPWtIx86da2L9w7uT+Tr/xgJbGwIkvTZTQ==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-node": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-3.3.0.tgz", + "integrity": "sha512-ulsT3EHF1RQ29X55cxBLgKsIKWni9JdbUqG7sipGVP4uhWcBpmm/vhKOMH340+27Acm9+kHGnN/5XmQ5LrIDgA==", + "license": "MIT", + "dependencies": { + "@azure/msal-common": "15.2.1", + "jsonwebtoken": "^9.0.0", + "uuid": "^8.3.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@azure/msal-node/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/@babel/code-frame": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", - "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", "dev": true, - "peer": true, + "license": "MIT", "dependencies": { - "@babel/highlight": "^7.23.4", - "chalk": "^2.4.2" + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/compat-data": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz", - "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz", + "integrity": "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==", "dev": true, - "peer": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.5.tgz", - "integrity": "sha512-Cwc2XjUrG4ilcfOw4wBAK+enbdgwAcAJCfGUItPBKR7Mjw4aEfAFYrLxeRp4jWgtNIKn3n2AlBOfwwafl+42/g==", + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.10.tgz", + "integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==", "dev": true, - "peer": true, + "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.5", - "@babel/helper-compilation-targets": "^7.22.15", - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helpers": "^7.23.5", - "@babel/parser": "^7.23.5", - "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.5", - "@babel/types": "^7.23.5", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.10", + "@babel/helper-compilation-targets": "^7.26.5", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.10", + "@babel/parser": "^7.26.10", + "@babel/template": "^7.26.9", + "@babel/traverse": "^7.26.10", + "@babel/types": "^7.26.10", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -93,10 +294,11 @@ } }, "node_modules/@babel/eslint-parser": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.23.3.tgz", - "integrity": "sha512-9bTuNlyx7oSstodm1cR1bECj4fkiknsDa1YniISkJemMY3DGhJNYBECbe6QD/q54mp2J8VO66jW3/7uP//iFCw==", + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.26.10.tgz", + "integrity": "sha512-QsfQZr4AiLpKqn7fz+j7SN+f43z2DZCgGyYbNJ2vJOqKfG4E6MZer1+jqGZqKJaxq/gdO2DC/nUu45+pOL5p2Q==", "dev": true, + "license": "MIT", "dependencies": { "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1", "eslint-visitor-keys": "^2.1.0", @@ -107,35 +309,36 @@ }, "peerDependencies": { "@babel/core": "^7.11.0", - "eslint": "^7.5.0 || ^8.0.0" + "eslint": "^7.5.0 || ^8.0.0 || ^9.0.0" } }, "node_modules/@babel/generator": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.5.tgz", - "integrity": "sha512-BPssCHrBD+0YrxviOa3QzpqwhNIXKEtOa2jQrm4FlmkC2apYgRnQcmPWiGZDlGxiNtltnUFolMe8497Esry+jA==", + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.10.tgz", + "integrity": "sha512-rRHT8siFIXQrAYOYqZQVsAr8vJ+cBNqcVAY6m5V8/4QqzaPl+zDBe6cLEPRDuNOUf3ww8RfJVlOyQMoSI+5Ang==", "dev": true, - "peer": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.23.5", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" + "@babel/parser": "^7.26.10", + "@babel/types": "^7.26.10", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", - "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz", + "integrity": "sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==", "dev": true, - "peer": true, + "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.22.9", - "@babel/helper-validator-option": "^7.22.15", - "browserslist": "^4.21.9", + "@babel/compat-data": "^7.26.5", + "@babel/helper-validator-option": "^7.25.9", + "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" }, @@ -143,206 +346,375 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "node_modules/@babel/helper-module-imports": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", + "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", "dev": true, - "peer": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "node_modules/@babel/helper-module-transforms": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", + "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", "dev": true, - "peer": true, + "license": "MIT", "dependencies": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "node_modules/@babel/helper-plugin-utils": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", + "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", "dev": true, - "peer": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/helper-module-imports": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", - "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", + "node_modules/@babel/helper-string-parser": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", "dev": true, - "peer": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", + "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.10.tgz", + "integrity": "sha512-UPYc3SauzZ3JGgj87GgZ89JVdC5dj0AoetR5Bw6wj4niittNyFh6+eOGonYvJ1ao6B8lEa3Q3klS7ADZ53bc5g==", + "dev": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.22.15" + "@babel/template": "^7.26.9", + "@babel/types": "^7.26.10" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", - "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", + "node_modules/@babel/parser": { + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.10.tgz", + "integrity": "sha512-6aQR2zGE/QFi8JpDLjUZEPYOs7+mhKXm86VaKFiLP35JQwQb6bwUE+XbvkH0EptsYhbNBSUGaUBLKqxH1xSgsA==", "dev": true, - "peer": true, + "license": "MIT", "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-simple-access": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.20" + "@babel/types": "^7.26.10" + }, + "bin": { + "parser": "bin/babel-parser.js" }, "engines": { - "node": ">=6.9.0" + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" }, "peerDependencies": { - "@babel/core": "^7.0.0" + "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/helper-simple-access": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", - "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", "dev": true, - "peer": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.22.5" + "@babel/helper-plugin-utils": "^7.8.0" }, - "engines": { - "node": ">=6.9.0" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", "dev": true, - "peer": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.22.5" + "@babel/helper-plugin-utils": "^7.12.13" }, - "engines": { - "node": ">=6.9.0" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/helper-string-parser": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", - "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", "dev": true, - "peer": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, "engines": { "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz", + "integrity": "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==", "dev": true, - "peer": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, "engines": { "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/helper-validator-option": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", - "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", "dev": true, - "peer": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz", + "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" + }, "engines": { "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/helpers": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.5.tgz", - "integrity": "sha512-oO7us8FzTEsG3U6ag9MfdF1iA/7Z6dz+MtFhifZk8C8o453rGJFFWUP1t+ULM9TUIAzC9uxXEiXjOiVMyd7QPg==", + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", "dev": true, - "peer": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "license": "MIT", "dependencies": { - "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.5", - "@babel/types": "^7.23.5" + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" }, "engines": { "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/highlight": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", - "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", "dev": true, - "peer": true, + "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0" + "@babel/helper-plugin-utils": "^7.14.5" }, "engines": { "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/parser": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.5.tgz", - "integrity": "sha512-hOOqoiNXrmGdFbhgCzu6GiURxUgM27Xwd/aPuu8RfHEZPBzL1Z54okAHAQjXfcQNwvrlkAmAp4SlRTZ45vlthQ==", + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz", + "integrity": "sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==", "dev": true, - "peer": true, - "bin": { - "parser": "bin/babel-parser.js" + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { - "node": ">=6.0.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.9.tgz", + "integrity": "sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==", "dev": true, - "peer": true, + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" + "@babel/code-frame": "^7.26.2", + "@babel/parser": "^7.26.9", + "@babel/types": "^7.26.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.5.tgz", - "integrity": "sha512-czx7Xy5a6sapWWRx61m1Ke1Ra4vczu1mCTtJam5zRTBOonfdJ+S/B6HYmGYu3fJtr8GGET3si6IhgWVBhJ/m8w==", - "dev": true, - "peer": true, - "dependencies": { - "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.5", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.5", - "@babel/types": "^7.23.5", - "debug": "^4.1.0", + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.10.tgz", + "integrity": "sha512-k8NuDrxr0WrPH5Aupqb2LCVURP/S0vBEn5mK6iH+GIYob66U5EtoZvcdudR2jQ4cmTwhEwW1DLB+Yyas9zjF6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.10", + "@babel/parser": "^7.26.10", + "@babel/template": "^7.26.9", + "@babel/types": "^7.26.10", + "debug": "^4.3.1", "globals": "^11.1.0" }, "engines": { @@ -350,20 +722,26 @@ } }, "node_modules/@babel/types": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.5.tgz", - "integrity": "sha512-ON5kSOJwVO6xXVRTvOI0eOnWe7VdUcIpsovGo9U/Br4Ie4UVFQTboO2cYnDhAGU6Fp+UxSiT+pMft0SMHfuq6w==", + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.10.tgz", + "integrity": "sha512-emqcG3vHrpxUKTrxcblR36dcrcoRDvKmnL/dCL6ZsHaShW80qxCAcNhzQZrpeM765VzEos+xOi4s+r4IXzTwdQ==", "dev": true, - "peer": true, + "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.23.4", - "@babel/helper-validator-identifier": "^7.22.20", - "to-fast-properties": "^2.0.0" + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" }, "engines": { "node": ">=6.9.0" } }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "license": "MIT" + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -392,14 +770,53 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", - "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", "dev": true, + "license": "MIT", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, + "node_modules/@eslint/config-array": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.2.tgz", + "integrity": "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.1.0.tgz", + "integrity": "sha512-kLrdPDJE1ckPo94kmPPf9Hfd0DU0Jw6oKYrhe+pwSC0iTUInmTa+w6fw8sGgcfkFJGNdWOUeOaDM4quW4a7OkA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz", + "integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/@eslint/eslintrc": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", @@ -439,1380 +856,5598 @@ } }, "node_modules/@eslint/js": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.55.0.tgz", - "integrity": "sha512-qQfo2mxH5yVom1kacMtZZJFVdW+E70mqHMJvVg6WTLo+VBuQJ4TojZlfWBjK0ve5BdEeNAVxOsl/nvNMpJOaJA==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", "dev": true, + "license": "MIT", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.13", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", - "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", "dev": true, - "dependencies": { - "@humanwhocodes/object-schema": "^2.0.1", - "debug": "^4.1.1", - "minimatch": "^3.0.5" - }, + "license": "Apache-2.0", "engines": { - "node": ">=10.10.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "node_modules/@eslint/plugin-kit": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.7.tgz", + "integrity": "sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g==", "dev": true, - "engines": { - "node": ">=12.22" + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.12.0", + "levn": "^0.4.1" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", - "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", - "dev": true + "node_modules/@faker-js/faker": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-8.4.1.tgz", + "integrity": "sha512-XQ3cU+Q8Uqmrbf2e0cIC/QN43sTBSC8KF12u29Mb47tWrt2hAgBXSgpZMj4Ao8Uk0iJcU99QsOCaIL8934obCg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/fakerjs" + } + ], + "license": "MIT", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0", + "npm": ">=6.14.13" + } }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", "dev": true, - "peer": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "license": "Apache-2.0", "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" }, "engines": { - "node": ">=6.0.0" + "node": ">=18.18.0" } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", "dev": true, - "peer": true, + "license": "Apache-2.0", "engines": { - "node": ">=6.0.0" + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", "dev": true, - "peer": true, + "license": "Apache-2.0", + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, "engines": { - "node": ">=6.0.0" + "node": ">=10.10.0" } }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, - "peer": true + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.20", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", - "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", "dev": true, - "peer": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } + "license": "BSD-3-Clause" }, - "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { - "version": "5.1.1-v1", - "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", - "integrity": "sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==", + "node_modules/@humanwhocodes/retry": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", + "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", "dev": true, - "dependencies": { - "eslint-scope": "5.1.1" + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", "dev": true, + "license": "ISC", "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" }, "engines": { - "node": ">= 8" + "node": ">=8" } }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, - "engines": { - "node": ">= 8" + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" } }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, + "license": "MIT", "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": ">= 8" + "node": ">=8" } }, - "node_modules/@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", - "dev": true - }, - "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "dev": true - }, - "node_modules/acorn": { - "version": "8.11.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", - "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", "dev": true, - "bin": { - "acorn": "bin/acorn" + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" }, - "engines": { - "node": ">=0.4.0" + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, + "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, "engines": { "node": ">=8" } }, - "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true, - "peer": true, - "dependencies": { - "color-convert": "^1.9.0" - }, + "license": "MIT", "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", - "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "node_modules/@jest/console": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "is-array-buffer": "^3.0.1" + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/array-includes": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz", - "integrity": "sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==", + "node_modules/@jest/core": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", - "is-string": "^1.0.7" + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" }, - "engines": { - "node": ">= 0.4" + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } } }, - "node_modules/array.prototype.findlastindex": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz", - "integrity": "sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA==", + "node_modules/@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0", - "get-intrinsic": "^1.2.1" + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/array.prototype.flat": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", - "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", + "node_modules/@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/array.prototype.flatmap": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", - "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" + "jest-get-type": "^29.6.3" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz", - "integrity": "sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==", + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", "dev": true, + "license": "MIT", "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", - "is-array-buffer": "^3.0.2", - "is-shared-array-buffer": "^1.0.2" + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "node_modules/@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", "dev": true, - "engines": { - "node": ">= 0.4" + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "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 - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/@jest/reporters": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", "dev": true, + "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } } }, - "node_modules/browserslist": { - "version": "4.22.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.2.tgz", - "integrity": "sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==", + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "peer": true, + "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001565", - "electron-to-chromium": "^1.4.601", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.13" + "@sinclair/typebox": "^0.27.8" }, - "bin": { - "browserslist": "cli.js" + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" }, "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/call-bind": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", - "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", + "node_modules/@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", "dev": true, + "license": "MIT", "dependencies": { - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.1", - "set-function-length": "^1.1.1" + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "node_modules/@jest/test-sequencer": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" + }, "engines": { - "node": ">=6" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/caniuse-lite": { - "version": "1.0.30001566", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001566.tgz", - "integrity": "sha512-ggIhCsTxmITBAMmK8yZjEhCO5/47jKXPu6Dha/wuCS4JePVL+3uiDEBuhu2aIoT+bqTOR8L76Ip1ARL9xYsEJA==", + "node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "peer": true + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } }, - "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", "dev": true, - "peer": true, + "license": "MIT", "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" }, "engines": { - "node": ">=4" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", "dev": true, - "peer": true, + "license": "MIT", "dependencies": { - "color-name": "1.1.3" + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" } }, - "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", "dev": true, - "peer": true + "engines": { + "node": ">=6.0.0" + } }, - "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 + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } }, - "node_modules/confusing-browser-globals": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", - "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==", + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", "dev": true }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dev": true, - "peer": true + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { + "version": "5.1.1-v1", + "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", + "integrity": "sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==", "dev": true, "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" + "eslint-scope": "5.1.1" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" }, "engines": { "node": ">= 8" } }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, "dependencies": { - "ms": "2.1.2" + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">= 8" } }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true, + "license": "MIT" }, - "node_modules/define-data-property": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", - "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", "dev": true, + "license": "MIT" + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" + "type-detect": "4.0.8" } }, - "node_modules/define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "@sinonjs/commons": "^3.0.0" } }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", "dev": true, + "license": "MIT", "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" } }, - "node_modules/electron-to-chromium": { - "version": "1.4.603", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.603.tgz", - "integrity": "sha512-Dvo5OGjnl7AZTU632dFJtWj0uJK835eeOVQIuRcmBmsFsTNn3cL05FqOyHAfGQDIoHfLhyJ1Tya3PJ0ceMz54g==", + "node_modules/@types/babel__generator": { + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", "dev": true, - "peer": true + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } }, - "node_modules/es-abstract": { - "version": "1.22.3", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", - "integrity": "sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==", + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", "dev": true, + "license": "MIT", "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "arraybuffer.prototype.slice": "^1.0.2", - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.5", - "es-set-tostringtag": "^2.0.1", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.2", - "get-symbol-description": "^1.0.0", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0", - "internal-slot": "^1.0.5", - "is-array-buffer": "^3.0.2", - "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.12", - "is-weakref": "^1.0.2", - "object-inspect": "^1.13.1", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.1", - "safe-array-concat": "^1.0.1", - "safe-regex-test": "^1.0.0", - "string.prototype.trim": "^1.2.8", - "string.prototype.trimend": "^1.0.7", - "string.prototype.trimstart": "^1.0.7", - "typed-array-buffer": "^1.0.0", - "typed-array-byte-length": "^1.0.0", - "typed-array-byte-offset": "^1.0.0", - "typed-array-length": "^1.0.4", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" } }, - "node_modules/es-set-tostringtag": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz", - "integrity": "sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==", + "node_modules/@types/babel__traverse": { + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", + "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", "dev": true, + "license": "MIT", "dependencies": { - "get-intrinsic": "^1.2.2", - "has-tostringtag": "^1.0.0", - "hasown": "^2.0.0" - }, - "engines": { - "node": ">= 0.4" + "@babel/types": "^7.20.7" } }, - "node_modules/es-shim-unscopables": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", - "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", "dev": true, + "license": "MIT", "dependencies": { - "hasown": "^2.0.0" + "@types/node": "*" } }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", "dev": true, + "license": "MIT", "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "@types/istanbul-lib-coverage": "*" } }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", "dev": true, - "peer": true, - "engines": { - "node": ">=6" + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" } }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "node_modules/@types/jest": { + "version": "29.5.14", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", + "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", "dev": true, - "peer": true, - "engines": { - "node": ">=0.8.0" + "license": "MIT", + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" } }, - "node_modules/eslint": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.55.0.tgz", - "integrity": "sha512-iyUUAM0PCKj5QpwGfmCAG9XXbZCWsqP/eWAWrG/W0umvjuLRBECwSFdt+rCntju0xEH7teIABPwXpahftIaTdA==", + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.13.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.10.tgz", + "integrity": "sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==", "dev": true, + "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.55.0", - "@humanwhocodes/config-array": "^0.11.13", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", + "undici-types": "~6.20.0" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.26.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.26.1.tgz", + "integrity": "sha512-2X3mwqsj9Bd3Ciz508ZUtoQQYpOhU/kWoUqIf49H8Z0+Vbh6UF/y0OEYp0Q0axOGzaBGs7QxRwq0knSQ8khQNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.26.1", + "@typescript-eslint/type-utils": "8.26.1", + "@typescript-eslint/utils": "8.26.1", + "@typescript-eslint/visitor-keys": "8.26.1", "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", + "ignore": "^5.3.1", "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" + "ts-api-utils": "^2.0.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.26.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.26.1.tgz", + "integrity": "sha512-w6HZUV4NWxqd8BdeFf81t07d7/YV9s7TCWrQQbG5uhuvGUAW+fq1usZ1Hmz9UPNLniFnD8GLSsDpjP0hm1S4lQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@typescript-eslint/scope-manager": "8.26.1", + "@typescript-eslint/types": "8.26.1", + "@typescript-eslint/typescript-estree": "8.26.1", + "@typescript-eslint/visitor-keys": "8.26.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.26.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.26.1.tgz", + "integrity": "sha512-6EIvbE5cNER8sqBu6V7+KeMZIC1664d2Yjt+B9EWUXrsyWpxx4lEZrmvxgSKRC6gX+efDL/UY9OpPZ267io3mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.26.1", + "@typescript-eslint/visitor-keys": "8.26.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.26.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.26.1.tgz", + "integrity": "sha512-Kcj/TagJLwoY/5w9JGEFV0dclQdyqw9+VMndxOJKtoFSjfZhLXhYjzsQEeyza03rwHx2vFEGvrJWJBXKleRvZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "8.26.1", + "@typescript-eslint/utils": "8.26.1", + "debug": "^4.3.4", + "ts-api-utils": "^2.0.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.26.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.26.1.tgz", + "integrity": "sha512-n4THUQW27VmQMx+3P+B0Yptl7ydfceUj4ON/AQILAASwgYdZ/2dhfymRMh5egRUrvK5lSmaOm77Ry+lmXPOgBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.26.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.26.1.tgz", + "integrity": "sha512-yUwPpUHDgdrv1QJ7YQal3cMVBGWfnuCdKbXw1yyjArax3353rEJP1ZA+4F8nOlQ3RfS2hUN/wze3nlY+ZOhvoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.26.1", + "@typescript-eslint/visitor-keys": "8.26.1", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.0.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "dev": true, + "license": "ISC", "bin": { - "eslint": "bin/eslint.js" + "semver": "bin/semver.js" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.26.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.26.1.tgz", + "integrity": "sha512-V4Urxa/XtSUroUrnI7q6yUTD3hDtfJ2jzVfeT3VK0ciizfK2q/zGC0iDh1lFMUZR8cImRrep6/q0xd/1ZGPQpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.26.1", + "@typescript-eslint/types": "8.26.1", + "@typescript-eslint/typescript-estree": "8.26.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://opencollective.com/eslint" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" } }, - "node_modules/eslint-config-airbnb-base": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz", - "integrity": "sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==", + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.26.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.26.1.tgz", + "integrity": "sha512-AjOC3zfnxd6S4Eiy3jwktJPclqhFHNyd8L6Gycf9WUPoKZpgM5PjkxY1X7uSy61xVpiJDhhk7XT2NVsN3ALTWg==", "dev": true, + "license": "MIT", "dependencies": { - "confusing-browser-globals": "^1.0.10", - "object.assign": "^4.1.2", - "object.entries": "^1.1.5", - "semver": "^6.3.0" + "@typescript-eslint/types": "8.26.1", + "eslint-visitor-keys": "^4.2.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, + "node_modules/acorn": { + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", + "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, "peerDependencies": { - "eslint": "^7.32.0 || ^8.2.0", - "eslint-plugin-import": "^2.25.2" + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.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==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "dev": true, + "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==", + "dev": true, + "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", + "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/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-includes": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", + "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz", + "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", + "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", + "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz", + "integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "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 + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "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/browserslist": { + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.1" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "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/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "license": "MIT", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "dev": true, + "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==", + "dev": true, + "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/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001703", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001703.tgz", + "integrity": "sha512-kRlAGTRWgPsOj7oARC9m1okJEXdL/8fekFVcxA8Hl7GH4r/sN4OJn/i6Flde373T50KS7Y37oFbMwlE8+F42kQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "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/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", + "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", + "dev": true, + "license": "MIT" + }, + "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==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", + "dev": true, + "license": "MIT" + }, + "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==", + "dev": true, + "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==", + "dev": true, + "license": "MIT" + }, + "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 + }, + "node_modules/confusing-browser-globals": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", + "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==", + "dev": true + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/dedent": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", + "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-browser": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz", + "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==", + "license": "MIT", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz", + "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dotenv": { + "version": "16.4.7", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", + "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", + "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", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "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/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/electron-to-chromium": { + "version": "1.5.115", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.115.tgz", + "integrity": "sha512-MN1nahVHAQMOz6dz6bNZ7apgqc9InZy7Ja4DBEVCTdeiUcegbyOYE9bi/f2Z/z6ZxLi0RxLpyJ3EGe+4h3w73A==", + "dev": true, + "license": "ISC" + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "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==", + "dev": true, + "license": "MIT" + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-abstract": { + "version": "1.23.9", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz", + "integrity": "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.0", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-regex": "^1.2.1", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.0", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.3", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.3", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.18" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "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/es-shim-unscopables": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", + "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-airbnb-base": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz", + "integrity": "sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==", + "dev": true, + "dependencies": { + "confusing-browser-globals": "^1.0.10", + "object.assign": "^4.1.2", + "object.entries": "^1.1.5", + "semver": "^6.3.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "peerDependencies": { + "eslint": "^7.32.0 || ^8.2.0", + "eslint-plugin-import": "^2.25.2" + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "dev": true, + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz", + "integrity": "sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.31.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz", + "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rtsao/scc": "^1.1.0", + "array-includes": "^3.1.8", + "array.prototype.findlastindex": "^1.2.5", + "array.prototype.flat": "^1.3.2", + "array.prototype.flatmap": "^1.3.2", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.12.0", + "hasown": "^2.0.2", + "is-core-module": "^2.15.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "object.groupby": "^1.0.3", + "object.values": "^1.2.0", + "semver": "^6.3.1", + "string.prototype.trimend": "^1.0.8", + "tsconfig-paths": "^3.15.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-jest": { + "version": "28.11.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-28.11.0.tgz", + "integrity": "sha512-QAfipLcNCWLVocVbZW8GimKn5p5iiMcgGbRzz8z/P5q7xw+cNEpYqyzFMtIF/ZgF2HLOyy+dYBut+DoYolvqig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/utils": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "engines": { + "node": "^16.10.0 || ^18.12.0 || >=20.0.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^6.0.0 || ^7.0.0 || ^8.0.0", + "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0", + "jest": "*" + }, + "peerDependenciesMeta": { + "@typescript-eslint/eslint-plugin": { + "optional": true + }, + "jest": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/eslint/node_modules/globals": { + "version": "13.23.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", + "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/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/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", + "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", + "dev": true + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "license": "MIT", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "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==", + "dev": true, + "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", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "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-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "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", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "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==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/husky": { + "version": "9.1.7", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz", + "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==", + "dev": true, + "license": "MIT", + "bin": { + "husky": "bin.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", + "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-proto": "^1.0.0", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-wsl": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", + "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.7.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^5.0.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-cli": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-leak-detector": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsbi": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/jsbi/-/jsbi-4.3.0.tgz", + "integrity": "sha512-SnZNcinB4RIcnEyZqFPdGPVgrg2AcnykiBy0sHVJQKHYeaLUvi3Exj+iaPpLnFVkDPZIV4U0yvgC9/R4uEAZ9g==", + "license": "Apache-2.0" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "license": "MIT", + "dependencies": { + "jws": "^3.2.2", + "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/jsonwebtoken/node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jsonwebtoken/node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "license": "MIT", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jsonwebtoken/node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "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.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "license": "MIT", + "dependencies": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "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/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint-import-resolver-node": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", - "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "node_modules/make-dir/node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "dev": true, - "dependencies": { - "debug": "^3.2.7", - "is-core-module": "^2.13.0", - "resolve": "^1.22.4" + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, - "node_modules/eslint-import-resolver-node/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "ms": "^2.1.1" + "tmpl": "1.0.5" } }, - "node_modules/eslint-module-utils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", - "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", + "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==", "dev": true, - "dependencies": { - "debug": "^3.2.7" - }, + "license": "MIT", "engines": { - "node": ">=4" - }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - } + "node": ">= 0.4" } }, - "node_modules/eslint-module-utils/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true, - "dependencies": { - "ms": "^2.1.1" + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" } }, - "node_modules/eslint-plugin-import": { - "version": "2.29.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.0.tgz", - "integrity": "sha512-QPOO5NO6Odv5lpoTkddtutccQjysJuFxoPS7fAHO+9m9udNHvTCPSAMW9zGAYj8lAIdr40I8yPCdUYrncXtrwg==", + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, + "license": "MIT", "dependencies": { - "array-includes": "^3.1.7", - "array.prototype.findlastindex": "^1.2.3", - "array.prototype.flat": "^1.3.2", - "array.prototype.flatmap": "^1.3.2", - "debug": "^3.2.7", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.9", - "eslint-module-utils": "^2.8.0", - "hasown": "^2.0.0", - "is-core-module": "^2.13.1", - "is-glob": "^4.0.3", - "minimatch": "^3.1.2", - "object.fromentries": "^2.0.7", - "object.groupby": "^1.0.1", - "object.values": "^1.1.7", - "semver": "^6.3.1", - "tsconfig-paths": "^3.14.2" + "braces": "^3.0.3", + "picomatch": "^2.3.1" }, "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + "node": ">=8.6" } }, - "node_modules/eslint-plugin-import/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true, - "dependencies": { - "ms": "^2.1.1" + "license": "MIT", + "engines": { + "node": ">=6" } }, - "node_modules/eslint-plugin-import/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "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, "dependencies": { - "esutils": "^2.0.2" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=0.10.0" + "node": "*" } }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/node-dependencies": { + "resolved": "node-dependencies", + "link": true + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "license": "MIT", "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" }, "engines": { - "node": ">=8.0.0" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" } }, - "node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true, + "license": "MIT" + }, + "node_modules/nodejs-debug": { + "resolved": "nodejs-debug", + "link": true + }, + "node_modules/nodejs-files": { + "resolved": "nodejs-files", + "link": true + }, + "node_modules/nodejs-http": { + "resolved": "nodejs-http", + "link": true + }, + "node_modules/nodejs-intro": { + "resolved": "nodejs-intro", + "link": true + }, + "node_modules/nodejs-intro-esm": { + "resolved": "nodejs-intro-esm", + "link": true + }, + "node_modules/nodejs-route": { + "resolved": "nodejs-route", + "link": true + }, + "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": ">=10" + "node": ">=0.10.0" } }, - "node_modules/eslint/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==", + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, + "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "path-key": "^3.0.0" }, "engines": { "node": ">=8" + } + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint/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==", + "node_modules/object.entries": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.7.tgz", + "integrity": "sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" }, "engines": { - "node": ">=7.0.0" + "node": ">= 0.4" } }, - "node_modules/eslint/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==", - "dev": true - }, - "node_modules/eslint/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "node_modules/object.groupby": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", "dev": true, + "license": "MIT", "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">= 0.4" } }, - "node_modules/eslint/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "node_modules/object.values": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">= 0.4" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "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==", "dev": true, - "engines": { - "node": ">=4.0" + "dependencies": { + "wrappy": "1" } }, - "node_modules/eslint/node_modules/globals": { - "version": "13.23.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", - "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, + "license": "MIT", "dependencies": { - "type-fest": "^0.20.2" + "mimic-fn": "^2.1.0" }, "engines": { - "node": ">=8" + "node": ">=6" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/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==", - "dev": true, + "node_modules/open": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.1.0.tgz", + "integrity": "sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw==", + "license": "MIT", + "dependencies": { + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^3.1.0" + }, "engines": { - "node": ">=8" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/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==", + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" }, "engines": { - "node": ">=8" + "node": ">= 0.8.0" } }, - "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", "dev": true, + "license": "MIT", "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">= 0.4" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=10" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "dependencies": { - "estraverse": "^5.1.0" + "p-limit": "^3.0.2" }, "engines": { - "node": ">=0.10" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/esquery/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true, + "license": "MIT", "engines": { - "node": ">=4.0" + "node": ">=6" } }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, "dependencies": { - "estraverse": "^5.2.0" + "callsites": "^3.0.0" }, "engines": { - "node": ">=4.0" + "node": ">=6" } }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, "engines": { - "node": ">=4.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, "engines": { - "node": ">=4.0" + "node": ">=8" } }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true + "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/fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, - "dependencies": { - "reusify": "^1.0.4" + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", "dev": true, - "dependencies": { - "flat-cache": "^3.0.4" - }, + "license": "MIT", "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">= 6" } }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, + "license": "MIT", "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" + "find-up": "^4.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, - "node_modules/flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, + "license": "MIT", "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=8" } }, - "node_modules/flatted": { - "version": "3.2.9", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", - "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", - "dev": true - }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, + "license": "MIT", "dependencies": { - "is-callable": "^1.1.3" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "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==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/function.prototype.name": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", - "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "functions-have-names": "^1.2.3" + "p-try": "^2.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=6" }, "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "peer": true, - "engines": { - "node": ">=6.9.0" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/get-intrinsic": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", - "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, + "license": "MIT", "dependencies": { - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "p-limit": "^2.2.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=8" } }, - "node_modules/get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - }, + "license": "MIT", "engines": { "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.0.tgz", + "integrity": "sha512-TQLvXjq5IAibjh8EpBIkNKxO749UEWABoiIZehEPiY4GNpVdhaFKqSTu+QrlU6D2dPAfubRmtJTi4K4YkQ5eXw==", + "bin": { + "prettier": "bin/prettier.cjs" }, "engines": { - "node": "*" + "node": ">=14" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, + "license": "MIT", "dependencies": { - "is-glob": "^4.0.3" + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" }, "engines": { - "node": ">=10.13.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, - "peer": true, + "license": "MIT", "engines": { - "node": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "node_modules/priorityqueuejs": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/priorityqueuejs/-/priorityqueuejs-2.0.0.tgz", + "integrity": "sha512-19BMarhgpq3x4ccvVi8k2QpJZcymo/iFUcrhPd4V96kYGovOdTsWwy7fxChYi4QY+m2EnGBWSX9Buakz+tWNQQ==", + "license": "MIT" + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", "dev": true, + "license": "MIT", "dependencies": { - "define-properties": "^1.1.3" + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 6" } }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=6" } }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" }, - "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, - "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==", + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true, - "peer": true, - "engines": { - "node": ">=4" - } + "license": "MIT" }, - "node_modules/has-property-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", - "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", "dev": true, + "license": "MIT", "dependencies": { - "get-intrinsic": "^1.2.2" + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, "engines": { "node": ">= 0.4" }, @@ -1820,182 +6455,202 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "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==", "dev": true, + "license": "MIT", "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.10.0" } }, - "node_modules/has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "dev": true, "dependencies": { - "has-symbols": "^1.0.2" + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" }, - "engines": { - "node": ">= 0.4" + "bin": { + "resolve": "bin/resolve" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/hasown": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", - "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", "dev": true, + "license": "MIT", "dependencies": { - "function-bind": "^1.1.2" + "resolve-from": "^5.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=8" } }, - "node_modules/ignore": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", - "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true, + "license": "MIT", "engines": { - "node": ">= 4" + "node": ">=8" } }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=4" } }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "node_modules/resolve.exports": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", + "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" + "node": ">=10" } }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/internal-slot": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.6.tgz", - "integrity": "sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==", + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.2", - "hasown": "^2.0.0", - "side-channel": "^1.0.4" - }, "engines": { - "node": ">= 0.4" + "iojs": ">=1.0.0", + "node": ">=0.10.0" } }, - "node_modules/is-array-buffer": { + "node_modules/rimraf": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", - "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", - "is-typed-array": "^1.1.10" + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dev": true, - "dependencies": { - "has-bigints": "^1.0.1" + "node_modules/run-applescript": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz", + "integrity": "sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==", + "license": "MIT", + "engines": { + "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "queue-microtask": "^1.2.2" } }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "node_modules/safe-array-concat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, "engines": { - "node": ">= 0.4" + "node": ">=0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-core-module": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "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/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", "dev": true, + "license": "MIT", "dependencies": { - "hasown": "^2.0.0" + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", "dev": true, + "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" }, "engines": { "node": ">= 0.4" @@ -2004,98 +6659,122 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, + "node_modules/semaphore": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/semaphore/-/semaphore-1.1.0.tgz", + "integrity": "sha512-O4OZEaNtkMd/K0i6js9SL+gqy0ZCBMgUvlSqHKi4IBdjhe7wB8pwztUk1BbZ1fmrvpwFrPbHzqd2w5pTcJH6LA==", "engines": { - "node": ">=0.10.0" + "node": ">=0.8.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==", + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "dev": true, + "license": "MIT", "dependencies": { - "is-extglob": "^2.1.1" + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" } }, - "node_modules/is-negative-zero": { + "node_modules/set-function-name": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, "engines": { "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", "dev": true, + "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, "engines": { "node": ">=8" } }, - "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "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==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2" + "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/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "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==", "dev": true, + "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" }, "engines": { "node": ">= 0.4" @@ -2104,13 +6783,17 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "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==", "dev": true, + "license": "MIT", "dependencies": { - "has-symbols": "^1.0.2" + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" }, "engines": { "node": ">= 0.4" @@ -2119,13 +6802,18 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-typed-array": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", - "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", + "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==", "dev": true, + "license": "MIT", "dependencies": { - "which-typed-array": "^1.1.11" + "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" @@ -2134,215 +6822,231 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true, - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "license": "ISC" }, - "node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true, + "license": "MIT" }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, - "peer": true + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", "dev": true, + "license": "MIT", "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" } }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true, - "peer": true, - "bin": { - "jsesc": "bin/jsesc" + "license": "BSD-3-Clause" + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" }, "engines": { - "node": ">=4" + "node": ">=10" } }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true + "node_modules/stoppable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz", + "integrity": "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==", + "license": "MIT", + "engines": { + "node": ">=4", + "npm": ">=6" + } }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", "dev": true, - "peer": true, - "bin": { - "json5": "lib/cli.js" + "license": "MIT", + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" }, "engines": { - "node": ">=6" + "node": ">=10" } }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "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==", "dev": true, + "license": "MIT", "dependencies": { - "json-buffer": "3.0.1" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" } }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "node_modules/string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", "dev": true, + "license": "MIT", "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" }, "engines": { - "node": ">= 0.8.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", "dev": true, + "license": "MIT", "dependencies": { - "p-locate": "^5.0.0" + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", "dev": true, - "peer": true, + "license": "MIT", "dependencies": { - "yallist": "^3.0.2" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "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==", "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" + "ansi-regex": "^5.0.1" }, "engines": { - "node": "*" + "node": ">=8" } }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "license": "MIT", + "engines": { + "node": ">=8" } }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "node_modules/node-releases": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", "dev": true, - "peer": true + "license": "MIT", + "engines": { + "node": ">=6" + } }, - "node_modules/object-inspect": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", - "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, + "engines": { + "node": ">=8" + }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "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==", "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, "engines": { - "node": ">= 0.4" + "node": ">=8" } }, - "node_modules/object.assign": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", - "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true, - "dependencies": { - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - }, "engines": { "node": ">= 0.4" }, @@ -2350,115 +7054,133 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/object.entries": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.7.tgz", - "integrity": "sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==", + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", "dev": true, + "license": "ISC", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" }, "engines": { - "node": ">= 0.4" + "node": ">=8" } }, - "node_modules/object.fromentries": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz", - "integrity": "sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==", + "node_modules/testrunner": { + "resolved": "testrunner", + "link": true + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", "dev": true, + "license": "BSD-3-Clause" + }, + "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": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "is-number": "^7.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=8.0" + } + }, + "node_modules/ts-api-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.1.tgz", + "integrity": "sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "typescript": ">=4.8.4" } }, - "node_modules/object.groupby": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.1.tgz", - "integrity": "sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ==", + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1" + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" } }, - "node_modules/object.values": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz", - "integrity": "sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==", + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "engines": { - "node": ">= 0.4" + "minimist": "^1.2.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "bin": { + "json5": "lib/cli.js" } }, - "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==", + "node_modules/tsconfig-paths/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true, - "dependencies": { - "wrappy": "1" + "license": "MIT", + "engines": { + "node": ">=4" } }, - "node_modules/optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "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/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, "dependencies": { - "@aashutoshrathi/word-wrap": "^1.2.3", - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" + "prelude-ls": "^1.2.1" }, "engines": { "node": ">= 0.8.0" } }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, + "license": "MIT", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=4" } }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, - "dependencies": { - "p-limit": "^3.0.2" - }, "engines": { "node": ">=10" }, @@ -2466,666 +7188,878 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", "dev": true, + "license": "MIT", "dependencies": { - "callsites": "^3.0.0" + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" }, "engines": { - "node": ">=6" + "node": ">= 0.4" } }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "node_modules/typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + }, "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true, - "peer": true - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "node_modules/typescript": { + "version": "5.8.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", + "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, "engines": { - "node": ">= 0.8.0" + "node": ">=14.17" } }, - "node_modules/prettier": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.0.tgz", - "integrity": "sha512-TQLvXjq5IAibjh8EpBIkNKxO749UEWABoiIZehEPiY4GNpVdhaFKqSTu+QrlU6D2dPAfubRmtJTi4K4YkQ5eXw==", - "bin": { - "prettier": "bin/prettier.cjs" + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" }, "engines": { - "node": ">=14" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", "dev": true, - "engines": { - "node": ">=6" - } + "license": "MIT" }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "node_modules/unit-testing": { + "resolved": "unit-testing", + "link": true + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", "dev": true, "funding": [ { - "type": "github", - "url": "https://github.com/sponsors/feross" + "type": "opencollective", + "url": "https://opencollective.com/browserslist" }, { - "type": "patreon", - "url": "https://www.patreon.com/feross" + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" }, - { - "type": "consulting", - "url": "https://feross.org/support" + { + "type": "github", + "url": "https://github.com/sponsors/ai" } - ] - }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", - "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==", - "dev": true, + ], + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "set-function-name": "^2.0.0" + "escalade": "^3.2.0", + "picocolors": "^1.1.1" }, - "engines": { - "node": ">= 0.4" + "bin": { + "update-browserslist-db": "cli.js" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "browserslist": ">= 4.21.0" } }, - "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, + "punycode": "^2.1.0" + } + }, + "node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "uuid": "dist/bin/uuid" } }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, "engines": { - "node": ">=4" + "node": ">=10.12.0" } }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", "dev": true, + "license": "Apache-2.0", + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "license": "MIT", "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" + "node": ">= 8" } }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "dependencies": { - "glob": "^7.1.3" + "isexe": "^2.0.0" }, "bin": { - "rimraf": "bin.js" + "node-which": "bin/node-which" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "engines": { + "node": ">= 8" } }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], + "license": "MIT", "dependencies": { - "queue-microtask": "^1.2.2" + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/safe-array-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz", - "integrity": "sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==", + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1", - "has-symbols": "^1.0.3", - "isarray": "^2.0.5" + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" }, "engines": { - "node": ">=0.4" + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/safe-regex-test": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", - "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "is-regex": "^1.1.4" + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "node_modules/which-typed-array": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", "dev": true, - "bin": { - "semver": "bin/semver.js" + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/set-function-length": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", - "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", + "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==", "dev": true, + "license": "MIT", "dependencies": { - "define-data-property": "^1.1.1", - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/set-function-name": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", - "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", + "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==", + "dev": true + }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", "dev": true, + "license": "ISC", "dependencies": { - "define-data-property": "^1.0.1", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.0" + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" }, "engines": { - "node": ">= 0.4" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, + "license": "MIT", "dependencies": { - "shebang-regex": "^3.0.0" + "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": ">=8" + "node": ">=12" } }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "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==", "dev": true, + "license": "ISC", "engines": { - "node": ">=8" + "node": ">=12" } }, - "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, - "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "engines": { + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/string.prototype.trim": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", - "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", + "node-dependencies": { + "version": "1.0.0", + "license": "MIT", + "devDependencies": { + "@babel/eslint-parser": "^7.23.3", + "eslint": "^8.54.0", + "eslint-config-airbnb-base": "^15.0.0", + "eslint-plugin-import": "^2.29.0", + "jest": "^29.7.0", + "prettier": "3.1.0" + } + }, + "nodejs-debug": { + "version": "1.0.0", + "license": "MIT", + "devDependencies": { + "@babel/eslint-parser": "^7.24.1", + "eslint": "^9.6.0", + "eslint-config-airbnb-base": "^15.0.0", + "eslint-plugin-import": "^2.29.0", + "prettier": "3.3.1" + } + }, + "nodejs-debug/node_modules/@eslint/eslintrc": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.0.tgz", + "integrity": "sha512-yaVPAiNAalnCZedKLdR21GOGILMLKPyqSLWaAjQFvYA2i/ciDi8ArYVr69Anohb6cH2Ukhqti4aFnYyPm8wdwQ==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" }, "engines": { - "node": ">= 0.4" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://opencollective.com/eslint" } }, - "node_modules/string.prototype.trimend": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", - "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", + "nodejs-debug/node_modules/@eslint/js": { + "version": "9.22.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.22.0.tgz", + "integrity": "sha512-vLFajx9o8d1/oL2ZkpMYbkLv8nDB6yaIwFNt7nI4+I80U/z03SxmfOMsLbvWr3p7C+Wnoh//aOu2pQW8cS0HCQ==", "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "nodejs-debug/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", - "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", + "nodejs-debug/node_modules/eslint": { + "version": "9.22.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.22.0.tgz", + "integrity": "sha512-9V/QURhsRN40xuHXWjV64yvrzMjcz7ZyNoF2jJFmy9j/SLk0u1OLSZgXi28MrXjymnjEGSR80WCdab3RGMDveQ==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.19.2", + "@eslint/config-helpers": "^0.1.0", + "@eslint/core": "^0.12.0", + "@eslint/eslintrc": "^3.3.0", + "@eslint/js": "9.22.0", + "@eslint/plugin-kit": "^0.2.7", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.3.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependenciesMeta": { + "jiti": { + "optional": true + } } }, - "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==", + "nodejs-debug/node_modules/eslint-scope": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz", + "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "ansi-regex": "^5.0.1" + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" }, "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, - "engines": { - "node": ">=4" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "nodejs-debug/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">=8" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://opencollective.com/eslint" } }, - "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==", + "nodejs-debug/node_modules/espree": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", "dev": true, - "peer": true, + "license": "BSD-2-Clause", "dependencies": { - "has-flag": "^3.0.0" + "acorn": "^8.14.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.0" }, "engines": { - "node": ">=4" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "engines": { - "node": ">= 0.4" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://opencollective.com/eslint" } }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "nodejs-debug/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, - "peer": true, + "license": "BSD-2-Clause", "engines": { - "node": ">=4" - } - }, - "node_modules/tsconfig-paths": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", - "integrity": "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==", - "dev": true, - "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" + "node": ">=4.0" } }, - "node_modules/tsconfig-paths/node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "nodejs-debug/node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, + "license": "MIT", "dependencies": { - "minimist": "^1.2.0" + "flat-cache": "^4.0.0" }, - "bin": { - "json5": "lib/cli.js" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "nodejs-debug/node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, + "license": "MIT", "dependencies": { - "prelude-ls": "^1.2.1" + "flatted": "^3.2.9", + "keyv": "^4.5.4" }, "engines": { - "node": ">= 0.8.0" + "node": ">=16" } }, - "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "nodejs-debug/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", "dev": true, + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/typed-array-buffer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", - "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", + "nodejs-debug/node_modules/prettier": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.1.tgz", + "integrity": "sha512-7CAwy5dRsxs8PHXT3twixW9/OEll8MLE0VRPCJyl7CkS6VHGPSlsVaWTiASPTyGyYRyApxlaWTzwUxVNrhcwDg==", "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1", - "is-typed-array": "^1.1.10" + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" }, "engines": { - "node": ">= 0.4" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/typed-array-byte-length": { + "nodejs-files": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", - "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", + "license": "MIT", + "devDependencies": { + "@babel/eslint-parser": "^7.24.1", + "eslint": "^8.57.0", + "eslint-config-airbnb-base": "^15.0.0", + "eslint-plugin-import": "^2.29.1", + "husky": "^9.0.10", + "prettier": "^3.3.1" + } + }, + "nodejs-files/node_modules/prettier": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", + "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" }, "engines": { - "node": ">= 0.4" + "node": ">=14" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/typed-array-byte-offset": { + "nodejs-http": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", - "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", + "license": "MIT", + "devDependencies": { + "@babel/eslint-parser": "^7.24.1", + "eslint": "^8.57.0", + "eslint-config-airbnb-base": "^15.0.0", + "eslint-plugin-import": "^2.29.1", + "husky": "^9.1.5", + "prettier": "^3.3.1" + } + }, + "nodejs-http/node_modules/prettier": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", + "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" }, "engines": { - "node": ">= 0.4" + "node": ">=14" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/typed-array-length": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", - "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", - "dev": true, + "nodejs-intro": { + "version": "1.0.0", + "license": "MIT", + "devDependencies": { + "@babel/eslint-parser": "^7.24.1", + "eslint": "^8.57.0", + "eslint-config-airbnb-base": "^15.0.0", + "eslint-plugin-import": "^2.29.0", + "prettier": "3.3.1" + } + }, + "nodejs-intro-esm": { + "version": "1.0.0", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "is-typed-array": "^1.1.9" + "node-fetch": "^3.3.2" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "devDependencies": { + "@babel/eslint-parser": "^7.24.1", + "eslint": "^8.57.0", + "eslint-config-airbnb-base": "^15.0.0", + "eslint-plugin-import": "^2.29.0", + "prettier": "3.2.4" } }, - "node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "nodejs-intro-esm/node_modules/prettier": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.4.tgz", + "integrity": "sha512-FWu1oLHKCrtpO1ypU6J0SbK2d9Ckwysq6bHj/uaCP26DxrPpppCLQRGVuqAxSTvhF00AcvDRyYrLNW7ocBhFFQ==", "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/update-browserslist-db": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", - "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "nodejs-intro/node_modules/prettier": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.1.tgz", + "integrity": "sha512-7CAwy5dRsxs8PHXT3twixW9/OEll8MLE0VRPCJyl7CkS6VHGPSlsVaWTiASPTyGyYRyApxlaWTzwUxVNrhcwDg==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "peer": true, - "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" - }, + "license": "MIT", "bin": { - "update-browserslist-db": "cli.js" + "prettier": "bin/prettier.cjs" }, - "peerDependencies": { - "browserslist": ">= 4.21.0" + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "dependencies": { - "punycode": "^2.1.0" + "nodejs-route": { + "version": "1.0.0", + "license": "MIT", + "devDependencies": { + "@babel/eslint-parser": "^7.23.3", + "eslint": "^8.56.0", + "eslint-config-airbnb-base": "^15.0.0", + "eslint-plugin-import": "^2.29.1", + "husky": "^9.0.10", + "prettier": "^3.2.4" } }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "nodejs-route/node_modules/prettier": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", + "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, + "license": "MIT", "bin": { - "node-which": "bin/node-which" + "prettier": "bin/prettier.cjs" }, "engines": { - "node": ">= 8" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dev": true, + "testrunner": { + "version": "1.0.0", + "license": "ISC", "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" + "@azure/cosmos": "^4.1.0", + "@azure/identity": "^4.4.1", + "dotenv": "^16.4.5", + "uuid": "^10.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "devDependencies": { + "@faker-js/faker": "^8.4.1", + "@types/node": "^22.4.0", + "@types/uuid": "^10.0.0", + "@typescript-eslint/eslint-plugin": "^8.2.0", + "eslint": "^8.56.0", + "eslint-config-airbnb-base": "^15.0.0", + "eslint-plugin-import": "^2.29.1", + "husky": "^9.0.10", + "prettier": "^3.2.4", + "typescript": "^5.5.4" } }, - "node_modules/which-typed-array": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz", - "integrity": "sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==", + "testrunner/node_modules/prettier": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", + "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.4", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" }, "engines": { - "node": ">= 0.4" + "node": ">=14" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "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==", - "dev": true - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true, - "peer": true + "unit-testing": { + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@azure/cosmos": "^4.1.0", + "@azure/identity": "^4.4.1", + "dotenv": "^16.4.5", + "uuid": "^10.0.0" + }, + "devDependencies": { + "@faker-js/faker": "^8.4.1", + "@types/jest": "^29.5.12", + "@types/node": "^22.4.0", + "@types/uuid": "^10.0.0", + "@typescript-eslint/eslint-plugin": "^8.2.0", + "eslint": "^8.56.0", + "eslint-config-airbnb-base": "^15.0.0", + "eslint-plugin-import": "^2.29.1", + "eslint-plugin-jest": "^28.8.0", + "husky": "^9.0.10", + "jest": "^29.7.0", + "prettier": "^3.2.4", + "typescript": "^5.5.4" + } }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "unit-testing/node_modules/prettier": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", + "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, "engines": { - "node": ">=10" + "node": ">=14" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/prettier/prettier?sponsor=1" } } } diff --git a/package.json b/package.json index 9afd1eba..63ff9efb 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "nodejs-http", "nodejs-intro", "nodejs-intro-esm", - "nodejs-route" + "nodejs-route", + "unit-testing" ] } diff --git a/testrunner/README.md b/testrunner/README.md new file mode 100644 index 00000000..41959ff5 --- /dev/null +++ b/testrunner/README.md @@ -0,0 +1,28 @@ +# Unit testing for the Azure SDK for JavaScript + +This subfolder is the source code for the [TBD article](). The purpose is to demonstrate unit test mocks for the Azure SDK for JavaScript. These specific examples use Azure Cosmos DB. + +## To run the test + +1. `npm install` +2. `npm run build` +3. `npm test` + + ```console + > unit-testing@1.0.0 test + > jest dist + + PASS dist/fakes/fake-in-mem-db.spec.js + PASS dist/mock-function/lib/insert.spec.js + + Test Suites: 2 passed, 2 total + Tests: 4 passed, 4 total + Snapshots: 0 total + Time: 4.247 s, estimated 5 s + Ran all test suites matching /dist/i. + ``` + +## Related content + +* [Passwordless connections for Azure services](https://learn.microsoft.com/azure/developer/intro/passwordless-overview) +* [Cosmos DB keyless access to the service](https://learn.microsoft.com/azure/cosmos-db/role-based-access-control) \ No newline at end of file diff --git a/testrunner/package.json b/testrunner/package.json new file mode 100644 index 00000000..b939b7b7 --- /dev/null +++ b/testrunner/package.json @@ -0,0 +1,13 @@ +{ + "name": "testrunner", + "version": "1.0.0", + "main": "index.js", + "type":"module", + "scripts": { + "test": "node --test", + "test:dev": "node --watch --test test/ " + }, + "directories": { + "test": "test" + } +} diff --git a/testrunner/test/01-spies.test.js b/testrunner/test/01-spies.test.js new file mode 100644 index 00000000..84b5d35f --- /dev/null +++ b/testrunner/test/01-spies.test.js @@ -0,0 +1,26 @@ +import { + describe, + it, + mock +} from 'node:test'; +import assert from 'node:assert'; + + +function run({fn, times}){ + for(let i = 0; i < times; i++){ + fn({current: i * 5}); + } +} + +describe('Spies', () => { + it('should verify calls in a mock', () => { + const spy = mock.fn(); + run({fn: spy, times: 2}); + + assert.strictEqual(spy.mock.callCount(), 3); + + expect(spy).toHaveBeenNthCalledWith(1, {current: 0}); + expect(spy).toHaveBeenNthCalledWith(2, {current: 5}); + expect(spy).toHaveBeenNthCalledWith(3, {current: 10}); + }); +}); \ No newline at end of file From 85a1f7b4bdc368ffeb328d79b0db521ece2f6b86 Mon Sep 17 00:00:00 2001 From: Dina Berry Date: Thu, 13 Mar 2025 14:48:54 +0000 Subject: [PATCH 02/27] ts with ignore all --- testrunner/package-lock.json | 30 +++++ testrunner/package.json | 5 +- testrunner/src/data/connect-to-cosmos.js | 46 +++++++ testrunner/src/data/connect-to-cosmos.ts | 36 +++++ testrunner/src/data/fake-data.js | 25 ++++ testrunner/src/data/fake-data.ts | 28 ++++ testrunner/src/data/model.js | 23 ++++ testrunner/src/data/model.ts | 42 ++++++ testrunner/src/data/verify.js | 9 ++ testrunner/src/data/verify.ts | 7 + testrunner/src/index.js | 27 ++++ testrunner/src/index.ts | 17 +++ testrunner/src/lib/insert.js | 42 ++++++ testrunner/src/lib/insert.spec.js | 103 +++++++++++++++ testrunner/src/lib/insert.spec.ts | 123 ++++++++++++++++++ testrunner/src/lib/insert.ts | 41 ++++++ .../01-spies.test.ts} | 10 +- testrunner/test/basics/02-stubs.test.ts | 120 +++++++++++++++++ .../boilerplate-with-mock.test-2.ts | 37 ++++++ .../boilerplate-with-mock.test.ts | 41 ++++++ .../test/test-boilerplate/boilerplate.test.ts | 28 ++++ testrunner/tsconfig.json | 19 +++ 22 files changed, 854 insertions(+), 5 deletions(-) create mode 100644 testrunner/package-lock.json create mode 100644 testrunner/src/data/connect-to-cosmos.js create mode 100644 testrunner/src/data/connect-to-cosmos.ts create mode 100644 testrunner/src/data/fake-data.js create mode 100644 testrunner/src/data/fake-data.ts create mode 100644 testrunner/src/data/model.js create mode 100644 testrunner/src/data/model.ts create mode 100644 testrunner/src/data/verify.js create mode 100644 testrunner/src/data/verify.ts create mode 100644 testrunner/src/index.js create mode 100644 testrunner/src/index.ts create mode 100644 testrunner/src/lib/insert.js create mode 100644 testrunner/src/lib/insert.spec.js create mode 100644 testrunner/src/lib/insert.spec.ts create mode 100644 testrunner/src/lib/insert.ts rename testrunner/test/{01-spies.test.js => basics/01-spies.test.ts} (56%) create mode 100644 testrunner/test/basics/02-stubs.test.ts create mode 100644 testrunner/test/test-boilerplate/boilerplate-with-mock.test-2.ts create mode 100644 testrunner/test/test-boilerplate/boilerplate-with-mock.test.ts create mode 100644 testrunner/test/test-boilerplate/boilerplate.test.ts create mode 100644 testrunner/tsconfig.json diff --git a/testrunner/package-lock.json b/testrunner/package-lock.json new file mode 100644 index 00000000..613ce82c --- /dev/null +++ b/testrunner/package-lock.json @@ -0,0 +1,30 @@ +{ + "name": "testrunner", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "testrunner", + "version": "1.0.0", + "dependencies": { + "@types/node": "^22.13.10" + } + }, + "node_modules/@types/node": { + "version": "22.13.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.10.tgz", + "integrity": "sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "license": "MIT" + } + } +} diff --git a/testrunner/package.json b/testrunner/package.json index b939b7b7..4143e256 100644 --- a/testrunner/package.json +++ b/testrunner/package.json @@ -2,12 +2,15 @@ "name": "testrunner", "version": "1.0.0", "main": "index.js", - "type":"module", "scripts": { + "build": "tsc", "test": "node --test", "test:dev": "node --watch --test test/ " }, "directories": { "test": "test" + }, + "dependencies": { + "@types/node": "^22.13.10" } } diff --git a/testrunner/src/data/connect-to-cosmos.js b/testrunner/src/data/connect-to-cosmos.js new file mode 100644 index 00000000..d416cea0 --- /dev/null +++ b/testrunner/src/data/connect-to-cosmos.js @@ -0,0 +1,46 @@ +"use strict"; +// connect-to-cosmos.ts +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Container = void 0; +exports.connectToCosmosWithoutKey = connectToCosmosWithoutKey; +exports.connectToContainer = connectToContainer; +exports.getUniqueId = getUniqueId; +const cosmos_1 = require("@azure/cosmos"); +Object.defineProperty(exports, "Container", { enumerable: true, get: function () { return cosmos_1.Container; } }); +const identity_1 = require("@azure/identity"); +require("dotenv/config"); +const uuid_1 = require("uuid"); +function connectToCosmosWithoutKey() { + const endpoint = process.env.COSMOS_DB_ENDPOINT; + const credential = new identity_1.DefaultAzureCredential(); + const client = new cosmos_1.CosmosClient({ endpoint, aadCredentials: credential }); + return client; +} +function connectToContainer() { + return __awaiter(this, void 0, void 0, function* () { + const client = connectToCosmosWithoutKey(); + const databaseName = process.env.COSMOS_DATABASE_NAME; + const containerName = process.env.COSMOS_CONTAINER_NAME; + // Ensure the database exists + const { database } = yield client.databases.createIfNotExists({ + id: databaseName, + }); + // Ensure the container exists + const { container } = yield database.containers.createIfNotExists({ + id: containerName, + }); + return container; + }); +} +function getUniqueId() { + return (0, uuid_1.v4)(); +} diff --git a/testrunner/src/data/connect-to-cosmos.ts b/testrunner/src/data/connect-to-cosmos.ts new file mode 100644 index 00000000..bc9d9108 --- /dev/null +++ b/testrunner/src/data/connect-to-cosmos.ts @@ -0,0 +1,36 @@ +// connect-to-cosmos.ts + +import { Container, CosmosClient } from '@azure/cosmos'; +import { DefaultAzureCredential } from '@azure/identity'; +import 'dotenv/config'; +import { v4 as uuidv4 } from 'uuid'; + +export { Container }; + +export function connectToCosmosWithoutKey() { + const endpoint = process.env.COSMOS_DB_ENDPOINT!; + const credential = new DefaultAzureCredential(); + + const client = new CosmosClient({ endpoint, aadCredentials: credential }); + return client; +} +export async function connectToContainer(): Promise { + const client = connectToCosmosWithoutKey(); + const databaseName = process.env.COSMOS_DATABASE_NAME; + const containerName = process.env.COSMOS_CONTAINER_NAME; + + // Ensure the database exists + const { database } = await client.databases.createIfNotExists({ + id: databaseName, + }); + + // Ensure the container exists + const { container } = await database.containers.createIfNotExists({ + id: containerName, + }); + + return container; +} +export function getUniqueId(): string { + return uuidv4(); +} diff --git a/testrunner/src/data/fake-data.js b/testrunner/src/data/fake-data.js new file mode 100644 index 00000000..1a79e9c4 --- /dev/null +++ b/testrunner/src/data/fake-data.js @@ -0,0 +1,25 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.createTestInput = createTestInput; +exports.createTestInputAndResult = createTestInputAndResult; +const uuid_1 = require("uuid"); +const faker_1 = require("@faker-js/faker"); +function createFixture() { + const result = { + first: faker_1.faker.person.firstName(), + last: faker_1.faker.person.lastName(), + }; + return result; +} +function createTestInput() { + const { first, last } = createFixture(); + return { id: (0, uuid_1.v4)(), first, last }; +} +function createTestInputAndResult() { + const input = createTestInput(); + const result = { + id: input.id, + name: `${input.first} ${input.last}`, + }; + return { input, result }; +} diff --git a/testrunner/src/data/fake-data.ts b/testrunner/src/data/fake-data.ts new file mode 100644 index 00000000..326912df --- /dev/null +++ b/testrunner/src/data/fake-data.ts @@ -0,0 +1,28 @@ +import { v4 as uuidv4 } from 'uuid'; +import { faker } from '@faker-js/faker'; +import { DbDocument, RawInput } from './model'; + +function createFixture(): T { + const result = { + first: faker.person.firstName(), + last: faker.person.lastName(), + }; + return result as T; +} + +export function createTestInput(): RawInput { + const { first, last } = createFixture(); + return { id: uuidv4(), first, last }; +} + +export function createTestInputAndResult(): { + input: RawInput; + result: Partial; +} { + const input = createTestInput(); + const result = { + id: input.id, + name: `${input.first} ${input.last}`, + }; + return { input, result }; +} diff --git a/testrunner/src/data/model.js b/testrunner/src/data/model.js new file mode 100644 index 00000000..77290d68 --- /dev/null +++ b/testrunner/src/data/model.js @@ -0,0 +1,23 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.isDbError = isDbError; +exports.isVerificationErrors = isVerificationErrors; +exports.validateRawInput = validateRawInput; +function isDbError(error) { + return 'message' in error && 'code' in error; +} +function isVerificationErrors(error) { + return 'message' in error; +} +function validateRawInput(input) { + const errors = []; + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + if (typeof input.first !== 'string' || input.first.trim().length === 0) { + errors.push('First name is required and must be a non-empty string'); + } + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + if (typeof input.last !== 'string' || input.last.trim().length === 0) { + errors.push('Last name is required and must be a non-empty string'); + } + return errors; +} diff --git a/testrunner/src/data/model.ts b/testrunner/src/data/model.ts new file mode 100644 index 00000000..cc747b54 --- /dev/null +++ b/testrunner/src/data/model.ts @@ -0,0 +1,42 @@ +// input-verified.ts +export interface DbDocument { + id: string; + name: string; +} + +export interface DbError { + message: string; + code: number; +} +export function isDbError(error: any): error is DbError { + return 'message' in error && 'code' in error; +} + +export interface VerificationErrors { + message: string; +} +export function isVerificationErrors(error: any): error is VerificationErrors { + return 'message' in error; +} + +export interface RawInput { + id: string; + first: string; + last: string; +} + +export function validateRawInput(input: any): string[] { + const errors: string[] = []; + + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + if (typeof input.first !== 'string' || input.first.trim().length === 0) { + errors.push('First name is required and must be a non-empty string'); + } + + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + if (typeof input.last !== 'string' || input.last.trim().length === 0) { + errors.push('Last name is required and must be a non-empty string'); + } + + return errors; +} diff --git a/testrunner/src/data/verify.js b/testrunner/src/data/verify.js new file mode 100644 index 00000000..d35618d0 --- /dev/null +++ b/testrunner/src/data/verify.js @@ -0,0 +1,9 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.inputVerified = inputVerified; +// input-verified.ts +const model_1 = require("./model"); +function inputVerified(doc) { + const result = (0, model_1.validateRawInput)(doc); + return result.length === 0 ? true : false; +} diff --git a/testrunner/src/data/verify.ts b/testrunner/src/data/verify.ts new file mode 100644 index 00000000..d71969d5 --- /dev/null +++ b/testrunner/src/data/verify.ts @@ -0,0 +1,7 @@ +// input-verified.ts +import { validateRawInput } from './model'; + +export function inputVerified(doc: any): boolean { + const result = validateRawInput(doc); + return result.length === 0 ? true : false; +} diff --git a/testrunner/src/index.js b/testrunner/src/index.js new file mode 100644 index 00000000..aa768ce8 --- /dev/null +++ b/testrunner/src/index.js @@ -0,0 +1,27 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const connect_to_cosmos_1 = require("./data/connect-to-cosmos"); +const fake_data_1 = require("./data/fake-data"); +const insert_1 = require("./lib/insert"); +function main() { + return __awaiter(this, void 0, void 0, function* () { + const container = yield (0, connect_to_cosmos_1.connectToContainer)(); + const input = (0, fake_data_1.createTestInput)(); + return yield (0, insert_1.insertDocument)(container, input); + }); +} +main() + .then((doc) => console.log(doc)) + .catch((error) => { + console.error(error); + process.exit(1); +}); diff --git a/testrunner/src/index.ts b/testrunner/src/index.ts new file mode 100644 index 00000000..1fb12681 --- /dev/null +++ b/testrunner/src/index.ts @@ -0,0 +1,17 @@ +import { connectToContainer } from './data/connect-to-cosmos'; +import { createTestInput } from './data/fake-data'; +import { DbDocument, DbError, VerificationErrors } from './data/model'; +import { insertDocument } from './lib/insert'; + +async function main(): Promise { + const container = await connectToContainer(); + const input = createTestInput(); + return await insertDocument(container, input); +} + +main() + .then((doc) => console.log(doc)) + .catch((error) => { + console.error(error); + process.exit(1); + }); diff --git a/testrunner/src/lib/insert.js b/testrunner/src/lib/insert.js new file mode 100644 index 00000000..fc9366b8 --- /dev/null +++ b/testrunner/src/lib/insert.js @@ -0,0 +1,42 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.insertDocument = insertDocument; +const verify_1 = require("../data/verify"); +function insertDocument(container, doc) { + return __awaiter(this, void 0, void 0, function* () { + const isVerified = (0, verify_1.inputVerified)(doc); + if (!isVerified) { + return { message: 'Verification failed' }; + } + try { + const { resource } = yield container.items.create({ + id: doc.id, + name: `${doc.first} ${doc.last}`, + }); + return resource; + } + catch (error) { + if (error instanceof Error) { + if (error.code === 409) { + return { + message: 'Insertion failed: Duplicate entry', + code: 409, + }; + } + return { message: error.message, code: error.code }; + } + else { + return { message: 'An unknown error occurred', code: 500 }; + } + } + }); +} diff --git a/testrunner/src/lib/insert.spec.js b/testrunner/src/lib/insert.spec.js new file mode 100644 index 00000000..ffebc739 --- /dev/null +++ b/testrunner/src/lib/insert.spec.js @@ -0,0 +1,103 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", { value: true }); +const fake_data_1 = require("../data/fake-data"); +const model_1 = require("../data/model"); +const verify_1 = require("../data/verify"); +const insert_1 = require("./insert"); +// Mock app dependencies for Cosmos DB setup +jest.mock('../data/connect-to-cosmos', () => ({ + connectToContainer: jest.fn(), + getUniqueId: jest.fn().mockReturnValue('unique-id'), +})); +// Mock app dependencies for input verification +jest.mock('../data/verify', () => ({ + inputVerified: jest.fn(), +})); +describe('insertDocument', () => { + // Mock the Cosmo DB Container object + let mockContainer; + beforeEach(() => { + // Clear all mocks before each test + jest.clearAllMocks(); + // Mock the Cosmos DB Container create method + mockContainer = { + items: { + create: jest.fn(), + }, + }; + }); + it('should return verification error if input is not verified', () => __awaiter(void 0, void 0, void 0, function* () { + // Arrange - Mock the input verification function to return false + jest.mocked(verify_1.inputVerified).mockReturnValue(false); + // Arrange - wrong shape of doc on purpose + const doc = { name: 'test' }; + // Act - Call the function to test + const insertDocumentResult = yield (0, insert_1.insertDocument)(mockContainer, doc); + // Assert - State verification: Check the result when verification fails + if ((0, model_1.isVerificationErrors)(insertDocumentResult)) { + expect(insertDocumentResult).toEqual({ + message: 'Verification failed', + }); + } + else { + throw new Error('Result is not of type VerificationErrors'); + } + // Assert - Behavior verification: Ensure create method was not called + expect(mockContainer.items.create).not.toHaveBeenCalled(); + })); + it('should insert document successfully', () => __awaiter(void 0, void 0, void 0, function* () { + // Arrange - create input and expected result data + const { input, result } = (0, fake_data_1.createTestInputAndResult)(); + // Arrange - mock the input verification function to return true + verify_1.inputVerified.mockReturnValue(true); + mockContainer.items.create.mockResolvedValue({ + resource: result, + }); + // Act - Call the function to test + const insertDocumentResult = yield (0, insert_1.insertDocument)(mockContainer, input); + // Assert - State verification: Check the result when insertion is successful + expect(insertDocumentResult).toEqual(result); + // Assert - Behavior verification: Ensure create method was called with correct arguments + expect(mockContainer.items.create).toHaveBeenCalledTimes(1); + expect(mockContainer.items.create).toHaveBeenCalledWith({ + id: input.id, + name: result.name, + }); + })); + it('should return error if db insert fails', () => __awaiter(void 0, void 0, void 0, function* () { + // Arrange - create input and expected result data + const { input, result } = (0, fake_data_1.createTestInputAndResult)(); + // Arrange - mock the input verification function to return true + jest.mocked(verify_1.inputVerified).mockReturnValue(true); + // Arrange - mock the Cosmos DB create method to throw an error + const mockError = { + message: 'An unknown error occurred', + code: 500, + }; + jest.mocked(mockContainer.items.create).mockRejectedValue(mockError); + // Act - Call the function to test + const insertDocumentResult = yield (0, insert_1.insertDocument)(mockContainer, input); + // Assert - verify type as DbError + if ((0, model_1.isDbError)(insertDocumentResult)) { + expect(insertDocumentResult.message).toBe(mockError.message); + } + else { + throw new Error('Result is not of type DbError'); + } + // Assert - Behavior verification: Ensure create method was called with correct arguments + expect(mockContainer.items.create).toHaveBeenCalledTimes(1); + expect(mockContainer.items.create).toHaveBeenCalledWith({ + id: input.id, + name: result.name, + }); + })); +}); diff --git a/testrunner/src/lib/insert.spec.ts b/testrunner/src/lib/insert.spec.ts new file mode 100644 index 00000000..659c37a4 --- /dev/null +++ b/testrunner/src/lib/insert.spec.ts @@ -0,0 +1,123 @@ +// insertDocument.test.ts +import { Container } from '../data/connect-to-cosmos'; +import { createTestInputAndResult } from '../data/fake-data'; +import { + DbDocument, + DbError, + isDbError, + isVerificationErrors, + RawInput, +} from '../data/model'; +import { inputVerified } from '../data/verify'; +import { insertDocument } from './insert'; + +// Mock app dependencies for Cosmos DB setup +jest.mock('../data/connect-to-cosmos', () => ({ + connectToContainer: jest.fn(), + getUniqueId: jest.fn().mockReturnValue('unique-id'), +})); + +// Mock app dependencies for input verification +jest.mock('../data/verify', () => ({ + inputVerified: jest.fn(), +})); + +describe('insertDocument', () => { + // Mock the Cosmo DB Container object + let mockContainer: jest.Mocked; + + beforeEach(() => { + // Clear all mocks before each test + jest.clearAllMocks(); + + // Mock the Cosmos DB Container create method + mockContainer = { + items: { + create: jest.fn(), + }, + } as unknown as jest.Mocked; + }); + + it('should return verification error if input is not verified', async () => { + // Arrange - Mock the input verification function to return false + jest.mocked(inputVerified).mockReturnValue(false); + + // Arrange - wrong shape of doc on purpose + const doc = { name: 'test' }; + + // Act - Call the function to test + const insertDocumentResult = await insertDocument( + mockContainer, + doc as unknown as RawInput, + ); + + // Assert - State verification: Check the result when verification fails + if (isVerificationErrors(insertDocumentResult)) { + expect(insertDocumentResult).toEqual({ + message: 'Verification failed', + }); + } else { + throw new Error('Result is not of type VerificationErrors'); + } + + // Assert - Behavior verification: Ensure create method was not called + expect(mockContainer.items.create).not.toHaveBeenCalled(); + }); + + it('should insert document successfully', async () => { + // Arrange - create input and expected result data + const { input, result }: { input: RawInput; result: Partial } = + createTestInputAndResult(); + + // Arrange - mock the input verification function to return true + (inputVerified as jest.Mock).mockReturnValue(true); + (mockContainer.items.create as jest.Mock).mockResolvedValue({ + resource: result, + }); + + // Act - Call the function to test + const insertDocumentResult = await insertDocument(mockContainer, input); + + // Assert - State verification: Check the result when insertion is successful + expect(insertDocumentResult).toEqual(result); + + // Assert - Behavior verification: Ensure create method was called with correct arguments + expect(mockContainer.items.create).toHaveBeenCalledTimes(1); + expect(mockContainer.items.create).toHaveBeenCalledWith({ + id: input.id, + name: result.name, + }); + }); + + it('should return error if db insert fails', async () => { + // Arrange - create input and expected result data + const { input, result } = createTestInputAndResult(); + + // Arrange - mock the input verification function to return true + jest.mocked(inputVerified).mockReturnValue(true); + + // Arrange - mock the Cosmos DB create method to throw an error + const mockError: DbError = { + message: 'An unknown error occurred', + code: 500, + }; + jest.mocked(mockContainer.items.create).mockRejectedValue(mockError); + + // Act - Call the function to test + const insertDocumentResult = await insertDocument(mockContainer, input); + + // Assert - verify type as DbError + if (isDbError(insertDocumentResult)) { + expect(insertDocumentResult.message).toBe(mockError.message); + } else { + throw new Error('Result is not of type DbError'); + } + + // Assert - Behavior verification: Ensure create method was called with correct arguments + expect(mockContainer.items.create).toHaveBeenCalledTimes(1); + expect(mockContainer.items.create).toHaveBeenCalledWith({ + id: input.id, + name: result.name, + }); + }); +}); diff --git a/testrunner/src/lib/insert.ts b/testrunner/src/lib/insert.ts new file mode 100644 index 00000000..8b9f8871 --- /dev/null +++ b/testrunner/src/lib/insert.ts @@ -0,0 +1,41 @@ +// insertDocument.ts +import { Container } from '../data/connect-to-cosmos'; +import { + DbDocument, + DbError, + RawInput, + VerificationErrors, +} from '../data/model'; +import { inputVerified } from '../data/verify'; + +export async function insertDocument( + container: Container, + doc: RawInput, +): Promise { + const isVerified: boolean = inputVerified(doc); + + if (!isVerified) { + return { message: 'Verification failed' } as VerificationErrors; + } + + try { + const { resource } = await container.items.create({ + id: doc.id, + name: `${doc.first} ${doc.last}`, + }); + + return resource as DbDocument; + } catch (error: any) { + if (error instanceof Error) { + if ((error as any).code === 409) { + return { + message: 'Insertion failed: Duplicate entry', + code: 409, + } as DbError; + } + return { message: error.message, code: (error as any).code } as DbError; + } else { + return { message: 'An unknown error occurred', code: 500 } as DbError; + } + } +} diff --git a/testrunner/test/01-spies.test.js b/testrunner/test/basics/01-spies.test.ts similarity index 56% rename from testrunner/test/01-spies.test.js rename to testrunner/test/basics/01-spies.test.ts index 84b5d35f..8bcbea13 100644 --- a/testrunner/test/01-spies.test.js +++ b/testrunner/test/basics/01-spies.test.ts @@ -15,12 +15,14 @@ function run({fn, times}){ describe('Spies', () => { it('should verify calls in a mock', () => { const spy = mock.fn(); - run({fn: spy, times: 2}); + run({fn: spy, times: 3}); assert.strictEqual(spy.mock.callCount(), 3); + const calls = spy.mock.calls; + + assert.deepStrictEqual(calls[0].arguments[0], {current: 0}); + assert.deepStrictEqual(calls[1].arguments[0], {current: 5}); + assert.deepStrictEqual(calls[2].arguments[0], {current: 10}); - expect(spy).toHaveBeenNthCalledWith(1, {current: 0}); - expect(spy).toHaveBeenNthCalledWith(2, {current: 5}); - expect(spy).toHaveBeenNthCalledWith(3, {current: 10}); }); }); \ No newline at end of file diff --git a/testrunner/test/basics/02-stubs.test.ts b/testrunner/test/basics/02-stubs.test.ts new file mode 100644 index 00000000..a9212958 --- /dev/null +++ b/testrunner/test/basics/02-stubs.test.ts @@ -0,0 +1,120 @@ +// @ts-nocheck + +import { + describe, + it, + beforeEach, + mock + } from 'node:test' + import assert from 'node:assert' + + class Service { + static async getTalks({ skip, limit }) { + const items = await fetch('https://tml-api.herokuapp.com/graphql', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + query: ` + { + getTalks (skip: ${skip}, limit: ${limit}) { + totalCount, + talks { + _id + title + } + } + } + ` + }) + }) + return (await items.json()).data.getTalks.talks + } + } + + function mapResponse(data) { + return data + .map(({ _id, title }, index) => `[${index}] id: ${_id}, title: ${title}`) + .join('\n') + } + + async function run({ skip = 0, limit = 10 }) { + const talks = mapResponse(await Service.getTalks({ skip, limit })) + return talks + } + + describe('Stub Test Suite', () => { + beforeEach(() => mock.restoreAll()) + + it('should stub APIs', async () => { + mock.method( + Service, + "getTalks", + ).mock.mockImplementation(async () => [ + { + _id: '63865750c839dbaacd8116e1', + title: 'The Journey About How I Fixed a Bug in the Node.js Core That Affected Thousands of Packages' + } + ]) + + const result = await run({ limit: 1 }) + const expected = `[0] id: 63865750c839dbaacd8116e1, title: The Journey About How I Fixed a Bug in the Node.js Core That Affected Thousands of Packages` + + assert.deepStrictEqual(Service.getTalks.mock.callCount(), 1) + const calls = Service.getTalks.mock.calls + + assert.deepStrictEqual(calls[0].arguments[0], { skip: 0, limit: 1 }) + assert.strictEqual(result, expected) + }) + + it('should stub different values for API calls', async () => { + const m = mock.method( + Service, + "getTalks", + ).mock + + m.mockImplementationOnce(async () => [ + { + _id: '63865750c839dbaacd8116e1', + title: 'The Journey About How I Fixed a Bug in the Node.js Core That Affected Thousands of Packages' + } + ], 0) + + m.mockImplementationOnce(async () => [ + { + _id: '01', + title: 'Mock 01' + } + ], 1) + + m.mockImplementationOnce(async () => [ + { + _id: '02', + title: 'Mock 02' + } + ], 2) + + { + const result = await run({ skip: 0, limit: 1 }) + const expected = `[0] id: 63865750c839dbaacd8116e1, title: The Journey About How I Fixed a Bug in the Node.js Core That Affected Thousands of Packages` + assert.strictEqual(result, expected) + } + { + const result = await run({ skip: 1, limit: 1 }) + const expected = `[0] id: 01, title: Mock 01` + assert.strictEqual(result, expected) + } + { + const result = await run({ skip: 2, limit: 1 }) + const expected = `[0] id: 02, title: Mock 02` + assert.strictEqual(result, expected) + } + + const calls = Service.getTalks.mock.calls + assert.strictEqual(Service.getTalks.mock.callCount(), 3) + assert.deepStrictEqual(calls[0].arguments[0], { skip: 0, limit: 1 }) + assert.deepStrictEqual(calls[1].arguments[0], { skip: 1, limit: 1 }) + assert.deepStrictEqual(calls[2].arguments[0], { skip: 2, limit: 1 }) + }) + }) \ No newline at end of file diff --git a/testrunner/test/test-boilerplate/boilerplate-with-mock.test-2.ts b/testrunner/test/test-boilerplate/boilerplate-with-mock.test-2.ts new file mode 100644 index 00000000..d3ff97cd --- /dev/null +++ b/testrunner/test/test-boilerplate/boilerplate-with-mock.test-2.ts @@ -0,0 +1,37 @@ +import { + describe, + it, + afterEach, + beforeEach, + mock + } from 'node:test' +import assert from 'node:assert'; + +import * as MyService from '../../src/data/connect-to-cosmos' + +describe('boilerplate with mock', () => { + beforeEach(() =>{ + // Setup required before each test + mock.restoreAll() + }) + afterEach(() => { + // Cleanup required after each test + }); + + it('should if ', async () => { + + mock.method(MyService, "getUniqueId").mock.mockImplementation(() => { + // Replace the original implementation with a mock result + + // return fake guid + return '12345678-1234-1234-1234-123456789012' + + }); + + const result = await MyService.getUniqueId(); + assert.strictEqual(result, + '12345678-1234-1234-1234-123456789012' + ) + + }) + }) \ No newline at end of file diff --git a/testrunner/test/test-boilerplate/boilerplate-with-mock.test.ts b/testrunner/test/test-boilerplate/boilerplate-with-mock.test.ts new file mode 100644 index 00000000..2203e74f --- /dev/null +++ b/testrunner/test/test-boilerplate/boilerplate-with-mock.test.ts @@ -0,0 +1,41 @@ +import { + describe, + it, + afterEach, + beforeEach, + mock + } from 'node:test' + import assert from 'node:assert' + + +// original value is 1 +const result = 1; + +class MyService { + static async myFunction() { + return Promise.resolve(result); + } +} + +describe('boilerplate with mock', () => { + beforeEach(() =>{ + // Setup required before each test + mock.restoreAll() + }) + afterEach(() => { + // Cleanup required after each test + }); + + it('should if ', async () => { + + // Replace the original implementation with a mock, returning 2 + mock.method(MyService, "myFunction").mock.mockImplementation(async () => Promise.resolve(2)) + + + // Test the function, but get the mocked value + const result = await MyService.myFunction(); + assert.strictEqual(result, + 2 + ) + }) + }) \ No newline at end of file diff --git a/testrunner/test/test-boilerplate/boilerplate.test.ts b/testrunner/test/test-boilerplate/boilerplate.test.ts new file mode 100644 index 00000000..ad6e054d --- /dev/null +++ b/testrunner/test/test-boilerplate/boilerplate.test.ts @@ -0,0 +1,28 @@ +import { + describe, + it, + afterEach, + beforeEach, + mock + } from 'node:test' + import assert from 'node:assert' + +describe('boilerplate', () => { + beforeEach(() =>{ + // Setup required before each test + }) + afterEach(() => { + // Cleanup required after each test + }); + + it('should if ', async () => { + // Arrange + // - set up the test data and the expected result + // Act + // - call the function to test + // Assert + // - check the state: result returned from function + // - check the behavior: dependency function calls + + }) + }) \ No newline at end of file diff --git a/testrunner/tsconfig.json b/testrunner/tsconfig.json new file mode 100644 index 00000000..eaff57f9 --- /dev/null +++ b/testrunner/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "es6", // Specify ECMAScript target version + "module": "commonjs", // Specify module code generation + "strict": true, // Enable all strict type-checking options + "esModuleInterop": true, // Enables emit interoperability between CommonJS and ES Modules + "noImplicitAny": false, // Enable error reporting for expressions and declarations with an implied 'any' type + "forceConsistentCasingInFileNames": true, // Disallow inconsistently-cased references to the same file + "outDir": "./dist", + "rootDir": "./", + }, + "include": [ + "src", + "test" + ], + "exclude": [ + "node_modules" + ] + } \ No newline at end of file From d03990a08392f9742052d12382cb4b640ab13cdf Mon Sep 17 00:00:00 2001 From: Dina Berry Date: Thu, 13 Mar 2025 15:27:27 +0000 Subject: [PATCH 03/27] working through tests --- testrunner/test/basics/02-stubs.test.ts | 5 +++-- .../test/test-boilerplate/boilerplate-with-mock.test-2.ts | 3 ++- .../test/test-boilerplate/boilerplate-with-mock.test.ts | 3 ++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/testrunner/test/basics/02-stubs.test.ts b/testrunner/test/basics/02-stubs.test.ts index a9212958..2672cce4 100644 --- a/testrunner/test/basics/02-stubs.test.ts +++ b/testrunner/test/basics/02-stubs.test.ts @@ -48,10 +48,11 @@ import { beforeEach(() => mock.restoreAll()) it('should stub APIs', async () => { - mock.method( + const m = mock.method( Service, "getTalks", - ).mock.mockImplementation(async () => [ + ).mock; + m.mockImplementation(async () => [ { _id: '63865750c839dbaacd8116e1', title: 'The Journey About How I Fixed a Bug in the Node.js Core That Affected Thousands of Packages' diff --git a/testrunner/test/test-boilerplate/boilerplate-with-mock.test-2.ts b/testrunner/test/test-boilerplate/boilerplate-with-mock.test-2.ts index d3ff97cd..261f5784 100644 --- a/testrunner/test/test-boilerplate/boilerplate-with-mock.test-2.ts +++ b/testrunner/test/test-boilerplate/boilerplate-with-mock.test-2.ts @@ -20,7 +20,8 @@ describe('boilerplate with mock', () => { it('should if ', async () => { - mock.method(MyService, "getUniqueId").mock.mockImplementation(() => { + const m = mock.method(MyService, "getUniqueId"); + m.mock.mockImplementation(() => { // Replace the original implementation with a mock result // return fake guid diff --git a/testrunner/test/test-boilerplate/boilerplate-with-mock.test.ts b/testrunner/test/test-boilerplate/boilerplate-with-mock.test.ts index 2203e74f..da8e0f5a 100644 --- a/testrunner/test/test-boilerplate/boilerplate-with-mock.test.ts +++ b/testrunner/test/test-boilerplate/boilerplate-with-mock.test.ts @@ -29,7 +29,8 @@ describe('boilerplate with mock', () => { it('should if ', async () => { // Replace the original implementation with a mock, returning 2 - mock.method(MyService, "myFunction").mock.mockImplementation(async () => Promise.resolve(2)) + const m = mock.method(MyService, "myFunction").mock; + m.mockImplementation(async () => Promise.resolve(2)) // Test the function, but get the mocked value From fb13af3d161b3ff184a816d6b6c8cacff7280610 Mon Sep 17 00:00:00 2001 From: Dina Berry Date: Thu, 13 Mar 2025 15:41:39 +0000 Subject: [PATCH 04/27] remove all autogen --- testrunner/src/data/connect-to-cosmos.js | 46 ---------- testrunner/src/data/fake-data.js | 25 ------ testrunner/src/data/model.js | 23 ----- testrunner/src/data/verify.js | 9 -- testrunner/src/index.js | 27 ------ testrunner/src/lib/insert.js | 42 --------- testrunner/src/lib/insert.spec.js | 103 ----------------------- testrunner/test/basics/02-stubs.test.ts | 1 + 8 files changed, 1 insertion(+), 275 deletions(-) delete mode 100644 testrunner/src/data/connect-to-cosmos.js delete mode 100644 testrunner/src/data/fake-data.js delete mode 100644 testrunner/src/data/model.js delete mode 100644 testrunner/src/data/verify.js delete mode 100644 testrunner/src/index.js delete mode 100644 testrunner/src/lib/insert.js delete mode 100644 testrunner/src/lib/insert.spec.js diff --git a/testrunner/src/data/connect-to-cosmos.js b/testrunner/src/data/connect-to-cosmos.js deleted file mode 100644 index d416cea0..00000000 --- a/testrunner/src/data/connect-to-cosmos.js +++ /dev/null @@ -1,46 +0,0 @@ -"use strict"; -// connect-to-cosmos.ts -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Container = void 0; -exports.connectToCosmosWithoutKey = connectToCosmosWithoutKey; -exports.connectToContainer = connectToContainer; -exports.getUniqueId = getUniqueId; -const cosmos_1 = require("@azure/cosmos"); -Object.defineProperty(exports, "Container", { enumerable: true, get: function () { return cosmos_1.Container; } }); -const identity_1 = require("@azure/identity"); -require("dotenv/config"); -const uuid_1 = require("uuid"); -function connectToCosmosWithoutKey() { - const endpoint = process.env.COSMOS_DB_ENDPOINT; - const credential = new identity_1.DefaultAzureCredential(); - const client = new cosmos_1.CosmosClient({ endpoint, aadCredentials: credential }); - return client; -} -function connectToContainer() { - return __awaiter(this, void 0, void 0, function* () { - const client = connectToCosmosWithoutKey(); - const databaseName = process.env.COSMOS_DATABASE_NAME; - const containerName = process.env.COSMOS_CONTAINER_NAME; - // Ensure the database exists - const { database } = yield client.databases.createIfNotExists({ - id: databaseName, - }); - // Ensure the container exists - const { container } = yield database.containers.createIfNotExists({ - id: containerName, - }); - return container; - }); -} -function getUniqueId() { - return (0, uuid_1.v4)(); -} diff --git a/testrunner/src/data/fake-data.js b/testrunner/src/data/fake-data.js deleted file mode 100644 index 1a79e9c4..00000000 --- a/testrunner/src/data/fake-data.js +++ /dev/null @@ -1,25 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.createTestInput = createTestInput; -exports.createTestInputAndResult = createTestInputAndResult; -const uuid_1 = require("uuid"); -const faker_1 = require("@faker-js/faker"); -function createFixture() { - const result = { - first: faker_1.faker.person.firstName(), - last: faker_1.faker.person.lastName(), - }; - return result; -} -function createTestInput() { - const { first, last } = createFixture(); - return { id: (0, uuid_1.v4)(), first, last }; -} -function createTestInputAndResult() { - const input = createTestInput(); - const result = { - id: input.id, - name: `${input.first} ${input.last}`, - }; - return { input, result }; -} diff --git a/testrunner/src/data/model.js b/testrunner/src/data/model.js deleted file mode 100644 index 77290d68..00000000 --- a/testrunner/src/data/model.js +++ /dev/null @@ -1,23 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.isDbError = isDbError; -exports.isVerificationErrors = isVerificationErrors; -exports.validateRawInput = validateRawInput; -function isDbError(error) { - return 'message' in error && 'code' in error; -} -function isVerificationErrors(error) { - return 'message' in error; -} -function validateRawInput(input) { - const errors = []; - // eslint-disable-next-line @typescript-eslint/no-unsafe-call - if (typeof input.first !== 'string' || input.first.trim().length === 0) { - errors.push('First name is required and must be a non-empty string'); - } - // eslint-disable-next-line @typescript-eslint/no-unsafe-call - if (typeof input.last !== 'string' || input.last.trim().length === 0) { - errors.push('Last name is required and must be a non-empty string'); - } - return errors; -} diff --git a/testrunner/src/data/verify.js b/testrunner/src/data/verify.js deleted file mode 100644 index d35618d0..00000000 --- a/testrunner/src/data/verify.js +++ /dev/null @@ -1,9 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.inputVerified = inputVerified; -// input-verified.ts -const model_1 = require("./model"); -function inputVerified(doc) { - const result = (0, model_1.validateRawInput)(doc); - return result.length === 0 ? true : false; -} diff --git a/testrunner/src/index.js b/testrunner/src/index.js deleted file mode 100644 index aa768ce8..00000000 --- a/testrunner/src/index.js +++ /dev/null @@ -1,27 +0,0 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const connect_to_cosmos_1 = require("./data/connect-to-cosmos"); -const fake_data_1 = require("./data/fake-data"); -const insert_1 = require("./lib/insert"); -function main() { - return __awaiter(this, void 0, void 0, function* () { - const container = yield (0, connect_to_cosmos_1.connectToContainer)(); - const input = (0, fake_data_1.createTestInput)(); - return yield (0, insert_1.insertDocument)(container, input); - }); -} -main() - .then((doc) => console.log(doc)) - .catch((error) => { - console.error(error); - process.exit(1); -}); diff --git a/testrunner/src/lib/insert.js b/testrunner/src/lib/insert.js deleted file mode 100644 index fc9366b8..00000000 --- a/testrunner/src/lib/insert.js +++ /dev/null @@ -1,42 +0,0 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.insertDocument = insertDocument; -const verify_1 = require("../data/verify"); -function insertDocument(container, doc) { - return __awaiter(this, void 0, void 0, function* () { - const isVerified = (0, verify_1.inputVerified)(doc); - if (!isVerified) { - return { message: 'Verification failed' }; - } - try { - const { resource } = yield container.items.create({ - id: doc.id, - name: `${doc.first} ${doc.last}`, - }); - return resource; - } - catch (error) { - if (error instanceof Error) { - if (error.code === 409) { - return { - message: 'Insertion failed: Duplicate entry', - code: 409, - }; - } - return { message: error.message, code: error.code }; - } - else { - return { message: 'An unknown error occurred', code: 500 }; - } - } - }); -} diff --git a/testrunner/src/lib/insert.spec.js b/testrunner/src/lib/insert.spec.js deleted file mode 100644 index ffebc739..00000000 --- a/testrunner/src/lib/insert.spec.js +++ /dev/null @@ -1,103 +0,0 @@ -"use strict"; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const fake_data_1 = require("../data/fake-data"); -const model_1 = require("../data/model"); -const verify_1 = require("../data/verify"); -const insert_1 = require("./insert"); -// Mock app dependencies for Cosmos DB setup -jest.mock('../data/connect-to-cosmos', () => ({ - connectToContainer: jest.fn(), - getUniqueId: jest.fn().mockReturnValue('unique-id'), -})); -// Mock app dependencies for input verification -jest.mock('../data/verify', () => ({ - inputVerified: jest.fn(), -})); -describe('insertDocument', () => { - // Mock the Cosmo DB Container object - let mockContainer; - beforeEach(() => { - // Clear all mocks before each test - jest.clearAllMocks(); - // Mock the Cosmos DB Container create method - mockContainer = { - items: { - create: jest.fn(), - }, - }; - }); - it('should return verification error if input is not verified', () => __awaiter(void 0, void 0, void 0, function* () { - // Arrange - Mock the input verification function to return false - jest.mocked(verify_1.inputVerified).mockReturnValue(false); - // Arrange - wrong shape of doc on purpose - const doc = { name: 'test' }; - // Act - Call the function to test - const insertDocumentResult = yield (0, insert_1.insertDocument)(mockContainer, doc); - // Assert - State verification: Check the result when verification fails - if ((0, model_1.isVerificationErrors)(insertDocumentResult)) { - expect(insertDocumentResult).toEqual({ - message: 'Verification failed', - }); - } - else { - throw new Error('Result is not of type VerificationErrors'); - } - // Assert - Behavior verification: Ensure create method was not called - expect(mockContainer.items.create).not.toHaveBeenCalled(); - })); - it('should insert document successfully', () => __awaiter(void 0, void 0, void 0, function* () { - // Arrange - create input and expected result data - const { input, result } = (0, fake_data_1.createTestInputAndResult)(); - // Arrange - mock the input verification function to return true - verify_1.inputVerified.mockReturnValue(true); - mockContainer.items.create.mockResolvedValue({ - resource: result, - }); - // Act - Call the function to test - const insertDocumentResult = yield (0, insert_1.insertDocument)(mockContainer, input); - // Assert - State verification: Check the result when insertion is successful - expect(insertDocumentResult).toEqual(result); - // Assert - Behavior verification: Ensure create method was called with correct arguments - expect(mockContainer.items.create).toHaveBeenCalledTimes(1); - expect(mockContainer.items.create).toHaveBeenCalledWith({ - id: input.id, - name: result.name, - }); - })); - it('should return error if db insert fails', () => __awaiter(void 0, void 0, void 0, function* () { - // Arrange - create input and expected result data - const { input, result } = (0, fake_data_1.createTestInputAndResult)(); - // Arrange - mock the input verification function to return true - jest.mocked(verify_1.inputVerified).mockReturnValue(true); - // Arrange - mock the Cosmos DB create method to throw an error - const mockError = { - message: 'An unknown error occurred', - code: 500, - }; - jest.mocked(mockContainer.items.create).mockRejectedValue(mockError); - // Act - Call the function to test - const insertDocumentResult = yield (0, insert_1.insertDocument)(mockContainer, input); - // Assert - verify type as DbError - if ((0, model_1.isDbError)(insertDocumentResult)) { - expect(insertDocumentResult.message).toBe(mockError.message); - } - else { - throw new Error('Result is not of type DbError'); - } - // Assert - Behavior verification: Ensure create method was called with correct arguments - expect(mockContainer.items.create).toHaveBeenCalledTimes(1); - expect(mockContainer.items.create).toHaveBeenCalledWith({ - id: input.id, - name: result.name, - }); - })); -}); diff --git a/testrunner/test/basics/02-stubs.test.ts b/testrunner/test/basics/02-stubs.test.ts index 2672cce4..6a9d6808 100644 --- a/testrunner/test/basics/02-stubs.test.ts +++ b/testrunner/test/basics/02-stubs.test.ts @@ -62,6 +62,7 @@ import { const result = await run({ limit: 1 }) const expected = `[0] id: 63865750c839dbaacd8116e1, title: The Journey About How I Fixed a Bug in the Node.js Core That Affected Thousands of Packages` + // TS2339: Property 'mock' does not exist on type assert.deepStrictEqual(Service.getTalks.mock.callCount(), 1) const calls = Service.getTalks.mock.calls From d05e2c5d8684d0b906fb5b2709a00efa31535239 Mon Sep 17 00:00:00 2001 From: Dina Berry Date: Thu, 13 Mar 2025 16:18:31 +0000 Subject: [PATCH 05/27] TS 2339 build error for mock resolved --- .../{test => src}/basics/01-spies.test.ts | 0 testrunner/src/basics/02-stubs.test.ts | 120 +++++++++++++++++ testrunner/src/data/connect-to-cosmos.ts | 36 ----- testrunner/src/data/fake-data.ts | 28 ---- testrunner/src/data/model.ts | 42 ------ testrunner/src/data/verify.ts | 7 - testrunner/src/fakes/fake-in-mem-db.test.ts | 74 +++++++++++ testrunner/src/index.ts | 17 --- testrunner/src/lib/insert.spec.ts | 123 ------------------ testrunner/src/lib/insert.ts | 41 ------ .../boilerplate-with-mock.test-2.ts.old} | 2 +- .../boilerplate-with-mock.test.ts | 0 .../test-boilerplate/boilerplate.test.ts | 0 testrunner/test/basics/02-stubs.test.ts | 122 ----------------- 14 files changed, 195 insertions(+), 417 deletions(-) rename testrunner/{test => src}/basics/01-spies.test.ts (100%) create mode 100644 testrunner/src/basics/02-stubs.test.ts delete mode 100644 testrunner/src/data/connect-to-cosmos.ts delete mode 100644 testrunner/src/data/fake-data.ts delete mode 100644 testrunner/src/data/model.ts delete mode 100644 testrunner/src/data/verify.ts create mode 100644 testrunner/src/fakes/fake-in-mem-db.test.ts delete mode 100644 testrunner/src/index.ts delete mode 100644 testrunner/src/lib/insert.spec.ts delete mode 100644 testrunner/src/lib/insert.ts rename testrunner/{test/test-boilerplate/boilerplate-with-mock.test-2.ts => src/test-boilerplate/boilerplate-with-mock.test-2.ts.old} (93%) rename testrunner/{test => src}/test-boilerplate/boilerplate-with-mock.test.ts (100%) rename testrunner/{test => src}/test-boilerplate/boilerplate.test.ts (100%) delete mode 100644 testrunner/test/basics/02-stubs.test.ts diff --git a/testrunner/test/basics/01-spies.test.ts b/testrunner/src/basics/01-spies.test.ts similarity index 100% rename from testrunner/test/basics/01-spies.test.ts rename to testrunner/src/basics/01-spies.test.ts diff --git a/testrunner/src/basics/02-stubs.test.ts b/testrunner/src/basics/02-stubs.test.ts new file mode 100644 index 00000000..d17bd85e --- /dev/null +++ b/testrunner/src/basics/02-stubs.test.ts @@ -0,0 +1,120 @@ +import { + describe, + it, + beforeEach, + mock +} from 'node:test' +import assert from 'node:assert' + +class Service { + static async getTalks({ skip, limit }) { + const items = await fetch('https://tml-api.herokuapp.com/graphql', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + query: ` + { + getTalks (skip: ${skip}, limit: ${limit}) { + totalCount, + talks { + _id + title + } + } + } + ` + }) + }) + return (await items.json()).data.getTalks.talks + } +} + +function mapResponse(data) { + return data + .map(({ _id, title }, index) => `[${index}] id: ${_id}, title: ${title}`) + .join('\n') +} + +async function run({ skip = 0, limit = 10 }) { + const talks = mapResponse(await Service.getTalks({ skip, limit })) + return talks +} + +describe('Stub Test Suite', () => { + beforeEach(() => mock.restoreAll()) + + it('should stub APIs', async () => { + const m = mock.method( + Service, + "getTalks", + ).mock; + m.mockImplementation(async () => [ + { + _id: '63865750c839dbaacd8116e1', + title: 'The Journey About How I Fixed a Bug in the Node.js Core That Affected Thousands of Packages' + } + ]) + + const result = await run({ limit: 1 }) + const expected = `[0] id: 63865750c839dbaacd8116e1, title: The Journey About How I Fixed a Bug in the Node.js Core That Affected Thousands of Packages` + + // TS2339: Property 'mock' does not exist on type + assert.deepStrictEqual(m.callCount(), 1) + const calls = m.calls + + assert.deepStrictEqual(calls[0].arguments[0], { skip: 0, limit: 1 }) + assert.strictEqual(result, expected) + }) + + it('should stub different values for API calls', async () => { + const m = mock.method( + Service, + "getTalks", + ).mock + + m.mockImplementationOnce(async () => [ + { + _id: '63865750c839dbaacd8116e1', + title: 'The Journey About How I Fixed a Bug in the Node.js Core That Affected Thousands of Packages' + } + ], 0) + + m.mockImplementationOnce(async () => [ + { + _id: '01', + title: 'Mock 01' + } + ], 1) + + m.mockImplementationOnce(async () => [ + { + _id: '02', + title: 'Mock 02' + } + ], 2) + + { + const result = await run({ skip: 0, limit: 1 }) + const expected = `[0] id: 63865750c839dbaacd8116e1, title: The Journey About How I Fixed a Bug in the Node.js Core That Affected Thousands of Packages` + assert.strictEqual(result, expected) + } + { + const result = await run({ skip: 1, limit: 1 }) + const expected = `[0] id: 01, title: Mock 01` + assert.strictEqual(result, expected) + } + { + const result = await run({ skip: 2, limit: 1 }) + const expected = `[0] id: 02, title: Mock 02` + assert.strictEqual(result, expected) + } + + const calls = m.calls + assert.strictEqual(m.callCount(), 3) + assert.deepStrictEqual(calls[0].arguments[0], { skip: 0, limit: 1 }) + assert.deepStrictEqual(calls[1].arguments[0], { skip: 1, limit: 1 }) + assert.deepStrictEqual(calls[2].arguments[0], { skip: 2, limit: 1 }) + }) +}) \ No newline at end of file diff --git a/testrunner/src/data/connect-to-cosmos.ts b/testrunner/src/data/connect-to-cosmos.ts deleted file mode 100644 index bc9d9108..00000000 --- a/testrunner/src/data/connect-to-cosmos.ts +++ /dev/null @@ -1,36 +0,0 @@ -// connect-to-cosmos.ts - -import { Container, CosmosClient } from '@azure/cosmos'; -import { DefaultAzureCredential } from '@azure/identity'; -import 'dotenv/config'; -import { v4 as uuidv4 } from 'uuid'; - -export { Container }; - -export function connectToCosmosWithoutKey() { - const endpoint = process.env.COSMOS_DB_ENDPOINT!; - const credential = new DefaultAzureCredential(); - - const client = new CosmosClient({ endpoint, aadCredentials: credential }); - return client; -} -export async function connectToContainer(): Promise { - const client = connectToCosmosWithoutKey(); - const databaseName = process.env.COSMOS_DATABASE_NAME; - const containerName = process.env.COSMOS_CONTAINER_NAME; - - // Ensure the database exists - const { database } = await client.databases.createIfNotExists({ - id: databaseName, - }); - - // Ensure the container exists - const { container } = await database.containers.createIfNotExists({ - id: containerName, - }); - - return container; -} -export function getUniqueId(): string { - return uuidv4(); -} diff --git a/testrunner/src/data/fake-data.ts b/testrunner/src/data/fake-data.ts deleted file mode 100644 index 326912df..00000000 --- a/testrunner/src/data/fake-data.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { v4 as uuidv4 } from 'uuid'; -import { faker } from '@faker-js/faker'; -import { DbDocument, RawInput } from './model'; - -function createFixture(): T { - const result = { - first: faker.person.firstName(), - last: faker.person.lastName(), - }; - return result as T; -} - -export function createTestInput(): RawInput { - const { first, last } = createFixture(); - return { id: uuidv4(), first, last }; -} - -export function createTestInputAndResult(): { - input: RawInput; - result: Partial; -} { - const input = createTestInput(); - const result = { - id: input.id, - name: `${input.first} ${input.last}`, - }; - return { input, result }; -} diff --git a/testrunner/src/data/model.ts b/testrunner/src/data/model.ts deleted file mode 100644 index cc747b54..00000000 --- a/testrunner/src/data/model.ts +++ /dev/null @@ -1,42 +0,0 @@ -// input-verified.ts -export interface DbDocument { - id: string; - name: string; -} - -export interface DbError { - message: string; - code: number; -} -export function isDbError(error: any): error is DbError { - return 'message' in error && 'code' in error; -} - -export interface VerificationErrors { - message: string; -} -export function isVerificationErrors(error: any): error is VerificationErrors { - return 'message' in error; -} - -export interface RawInput { - id: string; - first: string; - last: string; -} - -export function validateRawInput(input: any): string[] { - const errors: string[] = []; - - // eslint-disable-next-line @typescript-eslint/no-unsafe-call - if (typeof input.first !== 'string' || input.first.trim().length === 0) { - errors.push('First name is required and must be a non-empty string'); - } - - // eslint-disable-next-line @typescript-eslint/no-unsafe-call - if (typeof input.last !== 'string' || input.last.trim().length === 0) { - errors.push('Last name is required and must be a non-empty string'); - } - - return errors; -} diff --git a/testrunner/src/data/verify.ts b/testrunner/src/data/verify.ts deleted file mode 100644 index d71969d5..00000000 --- a/testrunner/src/data/verify.ts +++ /dev/null @@ -1,7 +0,0 @@ -// input-verified.ts -import { validateRawInput } from './model'; - -export function inputVerified(doc: any): boolean { - const result = validateRawInput(doc); - return result.length === 0 ? true : false; -} diff --git a/testrunner/src/fakes/fake-in-mem-db.test.ts b/testrunner/src/fakes/fake-in-mem-db.test.ts new file mode 100644 index 00000000..164a064d --- /dev/null +++ b/testrunner/src/fakes/fake-in-mem-db.test.ts @@ -0,0 +1,74 @@ +// fake-in-mem-db.spec.ts +import { describe, it, beforeEach, afterEach } from 'node:test'; +import assert from 'node:assert'; + +class FakeDatabase { + private data: Record; + + constructor() { + this.data = {}; + } + + save(key: string, value: any): void { + this.data[key] = value; + } + + get(key: string): any { + return this.data[key]; + } +} + +// Function to test +function someTestFunction(db: FakeDatabase, key: string, value: any): any { + db.save(key, value); + return db.get(key); +} + +describe('Test Fake in mem db', () => { + let fakeDb: FakeDatabase; + let testKey: string; + let testValue: any; + let originalSave: (key: string, value: any) => void; + let saveSpyCallCount: number; + let saveSpyArgs: Array<[string, any]>; + + beforeEach(() => { + fakeDb = new FakeDatabase(); + testKey = 'testKey'; + testValue = { + first: 'John', + last: 'Jones', + lastUpdated: new Date().toISOString(), + }; + + // Create a simple spy on the save method + originalSave = fakeDb.save.bind(fakeDb); + saveSpyCallCount = 0; + saveSpyArgs = []; + fakeDb.save = function (key: string, value: any): void { + saveSpyCallCount++; + saveSpyArgs.push([key, value]); + return originalSave(key, value); + }; + }); + + afterEach(() => { + // Restore original method if necessary + fakeDb.save = originalSave; + }); + + it('should save and return the correct value', () => { + // Call the function under test + const result = someTestFunction(fakeDb, testKey, testValue); + + // Verify state + assert.deepStrictEqual(result, testValue); + assert.strictEqual(result.first, 'John'); + assert.strictEqual(result.last, 'Jones'); + assert.strictEqual(result.lastUpdated, testValue.lastUpdated); + + // Verify behavior using our manual spy + assert.strictEqual(saveSpyCallCount, 1); + assert.deepStrictEqual(saveSpyArgs[0], [testKey, testValue]); + }); +}); \ No newline at end of file diff --git a/testrunner/src/index.ts b/testrunner/src/index.ts deleted file mode 100644 index 1fb12681..00000000 --- a/testrunner/src/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { connectToContainer } from './data/connect-to-cosmos'; -import { createTestInput } from './data/fake-data'; -import { DbDocument, DbError, VerificationErrors } from './data/model'; -import { insertDocument } from './lib/insert'; - -async function main(): Promise { - const container = await connectToContainer(); - const input = createTestInput(); - return await insertDocument(container, input); -} - -main() - .then((doc) => console.log(doc)) - .catch((error) => { - console.error(error); - process.exit(1); - }); diff --git a/testrunner/src/lib/insert.spec.ts b/testrunner/src/lib/insert.spec.ts deleted file mode 100644 index 659c37a4..00000000 --- a/testrunner/src/lib/insert.spec.ts +++ /dev/null @@ -1,123 +0,0 @@ -// insertDocument.test.ts -import { Container } from '../data/connect-to-cosmos'; -import { createTestInputAndResult } from '../data/fake-data'; -import { - DbDocument, - DbError, - isDbError, - isVerificationErrors, - RawInput, -} from '../data/model'; -import { inputVerified } from '../data/verify'; -import { insertDocument } from './insert'; - -// Mock app dependencies for Cosmos DB setup -jest.mock('../data/connect-to-cosmos', () => ({ - connectToContainer: jest.fn(), - getUniqueId: jest.fn().mockReturnValue('unique-id'), -})); - -// Mock app dependencies for input verification -jest.mock('../data/verify', () => ({ - inputVerified: jest.fn(), -})); - -describe('insertDocument', () => { - // Mock the Cosmo DB Container object - let mockContainer: jest.Mocked; - - beforeEach(() => { - // Clear all mocks before each test - jest.clearAllMocks(); - - // Mock the Cosmos DB Container create method - mockContainer = { - items: { - create: jest.fn(), - }, - } as unknown as jest.Mocked; - }); - - it('should return verification error if input is not verified', async () => { - // Arrange - Mock the input verification function to return false - jest.mocked(inputVerified).mockReturnValue(false); - - // Arrange - wrong shape of doc on purpose - const doc = { name: 'test' }; - - // Act - Call the function to test - const insertDocumentResult = await insertDocument( - mockContainer, - doc as unknown as RawInput, - ); - - // Assert - State verification: Check the result when verification fails - if (isVerificationErrors(insertDocumentResult)) { - expect(insertDocumentResult).toEqual({ - message: 'Verification failed', - }); - } else { - throw new Error('Result is not of type VerificationErrors'); - } - - // Assert - Behavior verification: Ensure create method was not called - expect(mockContainer.items.create).not.toHaveBeenCalled(); - }); - - it('should insert document successfully', async () => { - // Arrange - create input and expected result data - const { input, result }: { input: RawInput; result: Partial } = - createTestInputAndResult(); - - // Arrange - mock the input verification function to return true - (inputVerified as jest.Mock).mockReturnValue(true); - (mockContainer.items.create as jest.Mock).mockResolvedValue({ - resource: result, - }); - - // Act - Call the function to test - const insertDocumentResult = await insertDocument(mockContainer, input); - - // Assert - State verification: Check the result when insertion is successful - expect(insertDocumentResult).toEqual(result); - - // Assert - Behavior verification: Ensure create method was called with correct arguments - expect(mockContainer.items.create).toHaveBeenCalledTimes(1); - expect(mockContainer.items.create).toHaveBeenCalledWith({ - id: input.id, - name: result.name, - }); - }); - - it('should return error if db insert fails', async () => { - // Arrange - create input and expected result data - const { input, result } = createTestInputAndResult(); - - // Arrange - mock the input verification function to return true - jest.mocked(inputVerified).mockReturnValue(true); - - // Arrange - mock the Cosmos DB create method to throw an error - const mockError: DbError = { - message: 'An unknown error occurred', - code: 500, - }; - jest.mocked(mockContainer.items.create).mockRejectedValue(mockError); - - // Act - Call the function to test - const insertDocumentResult = await insertDocument(mockContainer, input); - - // Assert - verify type as DbError - if (isDbError(insertDocumentResult)) { - expect(insertDocumentResult.message).toBe(mockError.message); - } else { - throw new Error('Result is not of type DbError'); - } - - // Assert - Behavior verification: Ensure create method was called with correct arguments - expect(mockContainer.items.create).toHaveBeenCalledTimes(1); - expect(mockContainer.items.create).toHaveBeenCalledWith({ - id: input.id, - name: result.name, - }); - }); -}); diff --git a/testrunner/src/lib/insert.ts b/testrunner/src/lib/insert.ts deleted file mode 100644 index 8b9f8871..00000000 --- a/testrunner/src/lib/insert.ts +++ /dev/null @@ -1,41 +0,0 @@ -// insertDocument.ts -import { Container } from '../data/connect-to-cosmos'; -import { - DbDocument, - DbError, - RawInput, - VerificationErrors, -} from '../data/model'; -import { inputVerified } from '../data/verify'; - -export async function insertDocument( - container: Container, - doc: RawInput, -): Promise { - const isVerified: boolean = inputVerified(doc); - - if (!isVerified) { - return { message: 'Verification failed' } as VerificationErrors; - } - - try { - const { resource } = await container.items.create({ - id: doc.id, - name: `${doc.first} ${doc.last}`, - }); - - return resource as DbDocument; - } catch (error: any) { - if (error instanceof Error) { - if ((error as any).code === 409) { - return { - message: 'Insertion failed: Duplicate entry', - code: 409, - } as DbError; - } - return { message: error.message, code: (error as any).code } as DbError; - } else { - return { message: 'An unknown error occurred', code: 500 } as DbError; - } - } -} diff --git a/testrunner/test/test-boilerplate/boilerplate-with-mock.test-2.ts b/testrunner/src/test-boilerplate/boilerplate-with-mock.test-2.ts.old similarity index 93% rename from testrunner/test/test-boilerplate/boilerplate-with-mock.test-2.ts rename to testrunner/src/test-boilerplate/boilerplate-with-mock.test-2.ts.old index 261f5784..46aeb98a 100644 --- a/testrunner/test/test-boilerplate/boilerplate-with-mock.test-2.ts +++ b/testrunner/src/test-boilerplate/boilerplate-with-mock.test-2.ts.old @@ -7,7 +7,7 @@ import { } from 'node:test' import assert from 'node:assert'; -import * as MyService from '../../src/data/connect-to-cosmos' +import * as MyService from '../../data/connect-to-cosmos' describe('boilerplate with mock', () => { beforeEach(() =>{ diff --git a/testrunner/test/test-boilerplate/boilerplate-with-mock.test.ts b/testrunner/src/test-boilerplate/boilerplate-with-mock.test.ts similarity index 100% rename from testrunner/test/test-boilerplate/boilerplate-with-mock.test.ts rename to testrunner/src/test-boilerplate/boilerplate-with-mock.test.ts diff --git a/testrunner/test/test-boilerplate/boilerplate.test.ts b/testrunner/src/test-boilerplate/boilerplate.test.ts similarity index 100% rename from testrunner/test/test-boilerplate/boilerplate.test.ts rename to testrunner/src/test-boilerplate/boilerplate.test.ts diff --git a/testrunner/test/basics/02-stubs.test.ts b/testrunner/test/basics/02-stubs.test.ts deleted file mode 100644 index 6a9d6808..00000000 --- a/testrunner/test/basics/02-stubs.test.ts +++ /dev/null @@ -1,122 +0,0 @@ -// @ts-nocheck - -import { - describe, - it, - beforeEach, - mock - } from 'node:test' - import assert from 'node:assert' - - class Service { - static async getTalks({ skip, limit }) { - const items = await fetch('https://tml-api.herokuapp.com/graphql', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - query: ` - { - getTalks (skip: ${skip}, limit: ${limit}) { - totalCount, - talks { - _id - title - } - } - } - ` - }) - }) - return (await items.json()).data.getTalks.talks - } - } - - function mapResponse(data) { - return data - .map(({ _id, title }, index) => `[${index}] id: ${_id}, title: ${title}`) - .join('\n') - } - - async function run({ skip = 0, limit = 10 }) { - const talks = mapResponse(await Service.getTalks({ skip, limit })) - return talks - } - - describe('Stub Test Suite', () => { - beforeEach(() => mock.restoreAll()) - - it('should stub APIs', async () => { - const m = mock.method( - Service, - "getTalks", - ).mock; - m.mockImplementation(async () => [ - { - _id: '63865750c839dbaacd8116e1', - title: 'The Journey About How I Fixed a Bug in the Node.js Core That Affected Thousands of Packages' - } - ]) - - const result = await run({ limit: 1 }) - const expected = `[0] id: 63865750c839dbaacd8116e1, title: The Journey About How I Fixed a Bug in the Node.js Core That Affected Thousands of Packages` - - // TS2339: Property 'mock' does not exist on type - assert.deepStrictEqual(Service.getTalks.mock.callCount(), 1) - const calls = Service.getTalks.mock.calls - - assert.deepStrictEqual(calls[0].arguments[0], { skip: 0, limit: 1 }) - assert.strictEqual(result, expected) - }) - - it('should stub different values for API calls', async () => { - const m = mock.method( - Service, - "getTalks", - ).mock - - m.mockImplementationOnce(async () => [ - { - _id: '63865750c839dbaacd8116e1', - title: 'The Journey About How I Fixed a Bug in the Node.js Core That Affected Thousands of Packages' - } - ], 0) - - m.mockImplementationOnce(async () => [ - { - _id: '01', - title: 'Mock 01' - } - ], 1) - - m.mockImplementationOnce(async () => [ - { - _id: '02', - title: 'Mock 02' - } - ], 2) - - { - const result = await run({ skip: 0, limit: 1 }) - const expected = `[0] id: 63865750c839dbaacd8116e1, title: The Journey About How I Fixed a Bug in the Node.js Core That Affected Thousands of Packages` - assert.strictEqual(result, expected) - } - { - const result = await run({ skip: 1, limit: 1 }) - const expected = `[0] id: 01, title: Mock 01` - assert.strictEqual(result, expected) - } - { - const result = await run({ skip: 2, limit: 1 }) - const expected = `[0] id: 02, title: Mock 02` - assert.strictEqual(result, expected) - } - - const calls = Service.getTalks.mock.calls - assert.strictEqual(Service.getTalks.mock.callCount(), 3) - assert.deepStrictEqual(calls[0].arguments[0], { skip: 0, limit: 1 }) - assert.deepStrictEqual(calls[1].arguments[0], { skip: 1, limit: 1 }) - assert.deepStrictEqual(calls[2].arguments[0], { skip: 2, limit: 1 }) - }) - }) \ No newline at end of file From 86cd18a799f72a54954c74df12577b4df9e0aa4c Mon Sep 17 00:00:00 2001 From: Dina Berry Date: Thu, 13 Mar 2025 18:38:52 +0000 Subject: [PATCH 06/27] insert doesn't work --- testrunner/package.json | 6 +- testrunner/src/basics/02-stubs.test.ts | 1 - testrunner/src/fakes/fake-in-mem-db.test.ts | 2 +- .../mock-function/data/connect-to-cosmos.ts | 36 +++++ .../src/mock-function/data/fake-data.ts | 28 ++++ testrunner/src/mock-function/data/model.ts | 42 +++++ testrunner/src/mock-function/data/verify.ts | 7 + testrunner/src/mock-function/index.ts | 17 ++ .../src/mock-function/lib/insert.test.ts | 146 ++++++++++++++++++ testrunner/src/mock-function/lib/insert.ts | 41 +++++ ...ts.old => boilerplate-with-mock.test-2.ts} | 4 +- 11 files changed, 323 insertions(+), 7 deletions(-) create mode 100644 testrunner/src/mock-function/data/connect-to-cosmos.ts create mode 100644 testrunner/src/mock-function/data/fake-data.ts create mode 100644 testrunner/src/mock-function/data/model.ts create mode 100644 testrunner/src/mock-function/data/verify.ts create mode 100644 testrunner/src/mock-function/index.ts create mode 100644 testrunner/src/mock-function/lib/insert.test.ts create mode 100644 testrunner/src/mock-function/lib/insert.ts rename testrunner/src/test-boilerplate/{boilerplate-with-mock.test-2.ts.old => boilerplate-with-mock.test-2.ts} (87%) diff --git a/testrunner/package.json b/testrunner/package.json index 4143e256..856edfc5 100644 --- a/testrunner/package.json +++ b/testrunner/package.json @@ -3,9 +3,9 @@ "version": "1.0.0", "main": "index.js", "scripts": { - "build": "tsc", - "test": "node --test", - "test:dev": "node --watch --test test/ " + "build": "rm -rf dist && tsc", + "test": "npm run build && node --test", + "test:dev": "npm run build && node --watch --test test/ " }, "directories": { "test": "test" diff --git a/testrunner/src/basics/02-stubs.test.ts b/testrunner/src/basics/02-stubs.test.ts index d17bd85e..207abfe4 100644 --- a/testrunner/src/basics/02-stubs.test.ts +++ b/testrunner/src/basics/02-stubs.test.ts @@ -60,7 +60,6 @@ describe('Stub Test Suite', () => { const result = await run({ limit: 1 }) const expected = `[0] id: 63865750c839dbaacd8116e1, title: The Journey About How I Fixed a Bug in the Node.js Core That Affected Thousands of Packages` - // TS2339: Property 'mock' does not exist on type assert.deepStrictEqual(m.callCount(), 1) const calls = m.calls diff --git a/testrunner/src/fakes/fake-in-mem-db.test.ts b/testrunner/src/fakes/fake-in-mem-db.test.ts index 164a064d..103000d6 100644 --- a/testrunner/src/fakes/fake-in-mem-db.test.ts +++ b/testrunner/src/fakes/fake-in-mem-db.test.ts @@ -24,7 +24,7 @@ function someTestFunction(db: FakeDatabase, key: string, value: any): any { return db.get(key); } -describe('Test Fake in mem db', () => { +describe('In-Mem DB', () => { let fakeDb: FakeDatabase; let testKey: string; let testValue: any; diff --git a/testrunner/src/mock-function/data/connect-to-cosmos.ts b/testrunner/src/mock-function/data/connect-to-cosmos.ts new file mode 100644 index 00000000..bc9d9108 --- /dev/null +++ b/testrunner/src/mock-function/data/connect-to-cosmos.ts @@ -0,0 +1,36 @@ +// connect-to-cosmos.ts + +import { Container, CosmosClient } from '@azure/cosmos'; +import { DefaultAzureCredential } from '@azure/identity'; +import 'dotenv/config'; +import { v4 as uuidv4 } from 'uuid'; + +export { Container }; + +export function connectToCosmosWithoutKey() { + const endpoint = process.env.COSMOS_DB_ENDPOINT!; + const credential = new DefaultAzureCredential(); + + const client = new CosmosClient({ endpoint, aadCredentials: credential }); + return client; +} +export async function connectToContainer(): Promise { + const client = connectToCosmosWithoutKey(); + const databaseName = process.env.COSMOS_DATABASE_NAME; + const containerName = process.env.COSMOS_CONTAINER_NAME; + + // Ensure the database exists + const { database } = await client.databases.createIfNotExists({ + id: databaseName, + }); + + // Ensure the container exists + const { container } = await database.containers.createIfNotExists({ + id: containerName, + }); + + return container; +} +export function getUniqueId(): string { + return uuidv4(); +} diff --git a/testrunner/src/mock-function/data/fake-data.ts b/testrunner/src/mock-function/data/fake-data.ts new file mode 100644 index 00000000..326912df --- /dev/null +++ b/testrunner/src/mock-function/data/fake-data.ts @@ -0,0 +1,28 @@ +import { v4 as uuidv4 } from 'uuid'; +import { faker } from '@faker-js/faker'; +import { DbDocument, RawInput } from './model'; + +function createFixture(): T { + const result = { + first: faker.person.firstName(), + last: faker.person.lastName(), + }; + return result as T; +} + +export function createTestInput(): RawInput { + const { first, last } = createFixture(); + return { id: uuidv4(), first, last }; +} + +export function createTestInputAndResult(): { + input: RawInput; + result: Partial; +} { + const input = createTestInput(); + const result = { + id: input.id, + name: `${input.first} ${input.last}`, + }; + return { input, result }; +} diff --git a/testrunner/src/mock-function/data/model.ts b/testrunner/src/mock-function/data/model.ts new file mode 100644 index 00000000..cc747b54 --- /dev/null +++ b/testrunner/src/mock-function/data/model.ts @@ -0,0 +1,42 @@ +// input-verified.ts +export interface DbDocument { + id: string; + name: string; +} + +export interface DbError { + message: string; + code: number; +} +export function isDbError(error: any): error is DbError { + return 'message' in error && 'code' in error; +} + +export interface VerificationErrors { + message: string; +} +export function isVerificationErrors(error: any): error is VerificationErrors { + return 'message' in error; +} + +export interface RawInput { + id: string; + first: string; + last: string; +} + +export function validateRawInput(input: any): string[] { + const errors: string[] = []; + + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + if (typeof input.first !== 'string' || input.first.trim().length === 0) { + errors.push('First name is required and must be a non-empty string'); + } + + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + if (typeof input.last !== 'string' || input.last.trim().length === 0) { + errors.push('Last name is required and must be a non-empty string'); + } + + return errors; +} diff --git a/testrunner/src/mock-function/data/verify.ts b/testrunner/src/mock-function/data/verify.ts new file mode 100644 index 00000000..d71969d5 --- /dev/null +++ b/testrunner/src/mock-function/data/verify.ts @@ -0,0 +1,7 @@ +// input-verified.ts +import { validateRawInput } from './model'; + +export function inputVerified(doc: any): boolean { + const result = validateRawInput(doc); + return result.length === 0 ? true : false; +} diff --git a/testrunner/src/mock-function/index.ts b/testrunner/src/mock-function/index.ts new file mode 100644 index 00000000..1fb12681 --- /dev/null +++ b/testrunner/src/mock-function/index.ts @@ -0,0 +1,17 @@ +import { connectToContainer } from './data/connect-to-cosmos'; +import { createTestInput } from './data/fake-data'; +import { DbDocument, DbError, VerificationErrors } from './data/model'; +import { insertDocument } from './lib/insert'; + +async function main(): Promise { + const container = await connectToContainer(); + const input = createTestInput(); + return await insertDocument(container, input); +} + +main() + .then((doc) => console.log(doc)) + .catch((error) => { + console.error(error); + process.exit(1); + }); diff --git a/testrunner/src/mock-function/lib/insert.test.ts b/testrunner/src/mock-function/lib/insert.test.ts new file mode 100644 index 00000000..69e5ea9e --- /dev/null +++ b/testrunner/src/mock-function/lib/insert.test.ts @@ -0,0 +1,146 @@ +// insertDocument.test.ts +import { describe, it, beforeEach, mock } from 'node:test'; +import assert from 'node:assert'; +import { Container } from '../data/connect-to-cosmos'; +import { createTestInputAndResult } from '../data/fake-data'; +import { + DbError, + isDbError, + isVerificationErrors, + RawInput, +} from '../data/model'; +// Instead of jest.mock, import the whole module to override functions as needed. +import * as verifyModule from '../data/verify'; +import * as dbModule from '../data/connect-to-cosmos'; +import { insertDocument } from './insert'; + +// --- Test suite for insertDocument --- +describe('Insert into db', () => { + + // Set up a fresh container object before each test. + beforeEach(() => { + // Setup required before each test + mock.restoreAll() + }) + + /* + it('should insert document successfully', async () => { + // Arrange: override inputVerified to return true. + // Create local variables to capture calls to the container's create method. + + const fakeContainer = { + items: { + create: async (doc: any) => { + return { resource: result }; + }, + }, + } as unknown as Container; + + const mVerify = mock.method(verifyModule, "inputVerified").mock; + mVerify.mockImplementation(() => true); + + const mContainerCreate = mock.method(fakeContainer.items, "create").mock; + + // Arrange: get test input and expected result. + const { input, result } = createTestInputAndResult(); + + // Act: + const insertDocumentResult = await insertDocument(fakeContainer, input); + + // Assert - State verification. + assert.deepStrictEqual(insertDocumentResult, result); + + // Assert - Behavior verification: Ensure create was called once with correct arguments. + assert.strictEqual(mContainerCreate.callCount(), 1); + assert.deepStrictEqual(mContainerCreate.calls[0].arguments, { + id: input.id, + name: result.name, + }); + }); + */ +/* + describe("Insert failure", () => { + it('should return verification error if input is not verified', async () => { + + const fakeContainer = { + items: { + create: async (_: any) => { + throw new Error('Create method not implemented'); + }, + }, + } as unknown as Container; + + const mVerify = mock.method(verifyModule, "inputVerified").mock; + mVerify.mockImplementation(() => false); + + const mGetUniqueId = mock.method(dbModule, "getUniqueId").mock; + mGetUniqueId.mockImplementation(() => 'unique-id'); + + const mContainerCreate = mock.method(fakeContainer.items, "create").mock; + + // Arrange: wrong shape of document on purpose. + const doc = { name: 'test' } as unknown as RawInput; + + // Act: + const insertDocumentResult = await insertDocument(fakeContainer, doc); + + // Assert - State verification. + if (isVerificationErrors(insertDocumentResult)) { + assert.deepStrictEqual(insertDocumentResult, { message: 'Verification failed' }); + } else { + throw new Error('Result is not of type VerificationErrors'); + } + + // Assert - Behavior verification: Verify that create was never called. + assert.strictEqual(mContainerCreate.callCount(), 0); + + }); + });*/ + /* + it('should return error if db insert fails', async () => { + // Arrange: override inputVerified to return true. + let createCallCount = 0; + let createCallArgs: any[] = []; + let errorMessage: string = 'An unknown error occurred'; + + const fakeContainer = { + items: { + create: async (doc: any):Promise => { + return Promise.resolve(null); + }, + }, + } as unknown as Container; + + const mVerify = mock.method(verifyModule, "inputVerified").mock; + mVerify.mockImplementation(() => true); + + const mContainerCreate = mock.method(fakeContainer.items, "create").mock; + mContainerCreate.mockImplementation = async (doc: any) => { + const mockError: DbError = { + message: errorMessage, + code: 500, + }; + throw mockError; + } + + // Arrange: get test input and expected result. + const { input, result } = createTestInputAndResult(); + + // Act: + const insertDocumentResult = await insertDocument(fakeContainer, input); + + // Assert - Verify type as DbError. + if (isDbError(insertDocumentResult)) { + assert.strictEqual(insertDocumentResult.message, errorMessage); + } else { + throw new Error('Result is not of type DbError'); + } + + // Assert - Ensure create method was called once with the correct arguments. + assert.strictEqual(createCallCount, 1); + assert.deepStrictEqual(createCallArgs[0], { + id: input.id, + name: result.name, + }); + });*/ +}); \ No newline at end of file diff --git a/testrunner/src/mock-function/lib/insert.ts b/testrunner/src/mock-function/lib/insert.ts new file mode 100644 index 00000000..8b9f8871 --- /dev/null +++ b/testrunner/src/mock-function/lib/insert.ts @@ -0,0 +1,41 @@ +// insertDocument.ts +import { Container } from '../data/connect-to-cosmos'; +import { + DbDocument, + DbError, + RawInput, + VerificationErrors, +} from '../data/model'; +import { inputVerified } from '../data/verify'; + +export async function insertDocument( + container: Container, + doc: RawInput, +): Promise { + const isVerified: boolean = inputVerified(doc); + + if (!isVerified) { + return { message: 'Verification failed' } as VerificationErrors; + } + + try { + const { resource } = await container.items.create({ + id: doc.id, + name: `${doc.first} ${doc.last}`, + }); + + return resource as DbDocument; + } catch (error: any) { + if (error instanceof Error) { + if ((error as any).code === 409) { + return { + message: 'Insertion failed: Duplicate entry', + code: 409, + } as DbError; + } + return { message: error.message, code: (error as any).code } as DbError; + } else { + return { message: 'An unknown error occurred', code: 500 } as DbError; + } + } +} diff --git a/testrunner/src/test-boilerplate/boilerplate-with-mock.test-2.ts.old b/testrunner/src/test-boilerplate/boilerplate-with-mock.test-2.ts similarity index 87% rename from testrunner/src/test-boilerplate/boilerplate-with-mock.test-2.ts.old rename to testrunner/src/test-boilerplate/boilerplate-with-mock.test-2.ts index 46aeb98a..ded260d2 100644 --- a/testrunner/src/test-boilerplate/boilerplate-with-mock.test-2.ts.old +++ b/testrunner/src/test-boilerplate/boilerplate-with-mock.test-2.ts @@ -7,9 +7,9 @@ import { } from 'node:test' import assert from 'node:assert'; -import * as MyService from '../../data/connect-to-cosmos' +import * as MyService from '../mock-function/data/connect-to-cosmos' -describe('boilerplate with mock', () => { +describe('boilerplate with mock 2', () => { beforeEach(() =>{ // Setup required before each test mock.restoreAll() From 1edb6bfb563ae1e71509d1864564a77128f2e26b Mon Sep 17 00:00:00 2001 From: Dina Berry Date: Thu, 13 Mar 2025 20:19:21 +0000 Subject: [PATCH 07/27] coverage but still slow --- testrunner/README.md | 3 +- testrunner/package.json | 2 +- .../src/mock-function/lib/insert.test.ts | 117 +++++++++--------- 3 files changed, 60 insertions(+), 62 deletions(-) diff --git a/testrunner/README.md b/testrunner/README.md index 41959ff5..376bbee7 100644 --- a/testrunner/README.md +++ b/testrunner/README.md @@ -25,4 +25,5 @@ This subfolder is the source code for the [TBD article](). The purpose is to dem ## Related content * [Passwordless connections for Azure services](https://learn.microsoft.com/azure/developer/intro/passwordless-overview) -* [Cosmos DB keyless access to the service](https://learn.microsoft.com/azure/cosmos-db/role-based-access-control) \ No newline at end of file +* [Cosmos DB keyless access to the service](https://learn.microsoft.com/azure/cosmos-db/role-based-access-control) +* [Node.js Test Runner](https://nodejs.org/docs/latest/api/test.html#test-runner) \ No newline at end of file diff --git a/testrunner/package.json b/testrunner/package.json index 856edfc5..ea96876c 100644 --- a/testrunner/package.json +++ b/testrunner/package.json @@ -4,7 +4,7 @@ "main": "index.js", "scripts": { "build": "rm -rf dist && tsc", - "test": "npm run build && node --test", + "test": "npm run build && NODE_DEBUG_NATIVE=node:test NODE_DEBUG=node:test node --test --experimental-test-coverage --experimental-test-module-mocks", "test:dev": "npm run build && node --watch --test test/ " }, "directories": { diff --git a/testrunner/src/mock-function/lib/insert.test.ts b/testrunner/src/mock-function/lib/insert.test.ts index 69e5ea9e..31c884ef 100644 --- a/testrunner/src/mock-function/lib/insert.test.ts +++ b/testrunner/src/mock-function/lib/insert.test.ts @@ -4,6 +4,7 @@ import assert from 'node:assert'; import { Container } from '../data/connect-to-cosmos'; import { createTestInputAndResult } from '../data/fake-data'; import { + DbDocument, DbError, isDbError, isVerificationErrors, @@ -23,10 +24,10 @@ describe('Insert into db', () => { mock.restoreAll() }) - /* + test("Success", () => { it('should insert document successfully', async () => { // Arrange: override inputVerified to return true. - // Create local variables to capture calls to the container's create method. + const { input, result }: { input: RawInput; result: Partial } = createTestInputAndResult(); const fakeContainer = { items: { @@ -39,27 +40,27 @@ describe('Insert into db', () => { const mVerify = mock.method(verifyModule, "inputVerified").mock; mVerify.mockImplementation(() => true); - const mContainerCreate = mock.method(fakeContainer.items, "create").mock; - - // Arrange: get test input and expected result. - const { input, result } = createTestInputAndResult(); + const mContainerCreate = mock.method(fakeContainer.items as any, "create").mock; + mContainerCreate.mockImplementation(async (doc: any) => { + return { resource: result }; + }); // Act: - const insertDocumentResult = await insertDocument(fakeContainer, input); + const receivedResult = await insertDocument(fakeContainer, input); - // Assert - State verification. - assert.deepStrictEqual(insertDocumentResult, result); + // Assert - State verification: Ensure the result is as expected. + assert.deepStrictEqual(receivedResult, result); // Assert - Behavior verification: Ensure create was called once with correct arguments. assert.strictEqual(mContainerCreate.callCount(), 1); - assert.deepStrictEqual(mContainerCreate.calls[0].arguments, { + assert.deepStrictEqual(mContainerCreate.calls[0].arguments[0], { id: input.id, name: result.name, }); }); - */ -/* - describe("Insert failure", () => { + }); + + test("Failure", () => { it('should return verification error if input is not verified', async () => { const fakeContainer = { @@ -95,52 +96,48 @@ describe('Insert into db', () => { assert.strictEqual(mContainerCreate.callCount(), 0); }); - });*/ - /* - it('should return error if db insert fails', async () => { - // Arrange: override inputVerified to return true. - let createCallCount = 0; - let createCallArgs: any[] = []; - let errorMessage: string = 'An unknown error occurred'; - - const fakeContainer = { - items: { - create: async (doc: any):Promise => { - return Promise.resolve(null); - }, + }); + + it('should return error if db insert fails', async () => { + // Arrange: override inputVerified to return true. + const { input, result } = createTestInputAndResult(); + let errorMessage: string = 'An unknown error occurred'; + + const fakeContainer = { + items: { + create: async (doc: any): Promise => { + return Promise.resolve(null); }, - } as unknown as Container; - - const mVerify = mock.method(verifyModule, "inputVerified").mock; - mVerify.mockImplementation(() => true); - - const mContainerCreate = mock.method(fakeContainer.items, "create").mock; - mContainerCreate.mockImplementation = async (doc: any) => { - const mockError: DbError = { - message: errorMessage, - code: 500, - }; - throw mockError; - } - - // Arrange: get test input and expected result. - const { input, result } = createTestInputAndResult(); - - // Act: - const insertDocumentResult = await insertDocument(fakeContainer, input); - - // Assert - Verify type as DbError. - if (isDbError(insertDocumentResult)) { - assert.strictEqual(insertDocumentResult.message, errorMessage); - } else { - throw new Error('Result is not of type DbError'); - } - - // Assert - Ensure create method was called once with the correct arguments. - assert.strictEqual(createCallCount, 1); - assert.deepStrictEqual(createCallArgs[0], { - id: input.id, - name: result.name, - }); - });*/ + }, + } as unknown as Container; + + const mVerify = mock.method(verifyModule, "inputVerified").mock; + mVerify.mockImplementation(() => true); + + const mContainerCreate = mock.method(fakeContainer.items, "create").mock; + mContainerCreate.mockImplementation = async (doc: any) => { + const mockError: DbError = { + message: errorMessage, + code: 500, + }; + throw mockError; + } + + // Act: + const insertDocumentResult = await insertDocument(fakeContainer, input); + + // Assert - Verify type as DbError. + if (isDbError(insertDocumentResult)) { + assert.strictEqual(insertDocumentResult.message, errorMessage); + } else { + throw new Error('Result is not of type DbError'); + } + + // Assert - Ensure create method was called once with the correct arguments. + assert.strictEqual(mContainerCreate.callCount(), 1); + assert.deepStrictEqual(mContainerCreate.calls[0].arguments[0], { + id: input.id, + name: result.name, + }); + }); }); \ No newline at end of file From 363710c75e1263d9d03f42ff3fd08a82baf88542 Mon Sep 17 00:00:00 2001 From: Dina Berry Date: Thu, 13 Mar 2025 20:28:28 +0000 Subject: [PATCH 08/27] .nvmrc file --- .nvmrc | 1 + 1 file changed, 1 insertion(+) create mode 100644 .nvmrc diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 00000000..dc0bb0f4 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +v22.12.0 From be19363bfc90af5dc21b350309feb5af310dff65 Mon Sep 17 00:00:00 2001 From: "Dina Berry (MSFT)" Date: Thu, 13 Mar 2025 13:31:10 -0700 Subject: [PATCH 09/27] Update testrunner/tsconfig.json Co-authored-by: Augustin Mauroy <97875033+AugustinMauroy@users.noreply.github.com> Signed-off-by: Dina Berry (MSFT) --- testrunner/tsconfig.json | 1 + 1 file changed, 1 insertion(+) diff --git a/testrunner/tsconfig.json b/testrunner/tsconfig.json index eaff57f9..4aeff363 100644 --- a/testrunner/tsconfig.json +++ b/testrunner/tsconfig.json @@ -8,6 +8,7 @@ "forceConsistentCasingInFileNames": true, // Disallow inconsistently-cased references to the same file "outDir": "./dist", "rootDir": "./", + "erasableSyntaxOnly": true, }, "include": [ "src", From ed12972e5b5e935d6abc0ff7c96edbe8916c31e2 Mon Sep 17 00:00:00 2001 From: "Dina Berry (MSFT)" Date: Thu, 13 Mar 2025 13:31:15 -0700 Subject: [PATCH 10/27] Update testrunner/package.json Co-authored-by: Augustin Mauroy <97875033+AugustinMauroy@users.noreply.github.com> Signed-off-by: Dina Berry (MSFT) --- testrunner/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/testrunner/package.json b/testrunner/package.json index ea96876c..00922cb5 100644 --- a/testrunner/package.json +++ b/testrunner/package.json @@ -2,6 +2,7 @@ "name": "testrunner", "version": "1.0.0", "main": "index.js", + "type": "module", "scripts": { "build": "rm -rf dist && tsc", "test": "npm run build && NODE_DEBUG_NATIVE=node:test NODE_DEBUG=node:test node --test --experimental-test-coverage --experimental-test-module-mocks", From 3e86589d5f56c2cceb6230b39bafa89d59650f0a Mon Sep 17 00:00:00 2001 From: "Dina Berry (MSFT)" Date: Thu, 13 Mar 2025 13:31:24 -0700 Subject: [PATCH 11/27] Update testrunner/package.json Co-authored-by: Augustin Mauroy <97875033+AugustinMauroy@users.noreply.github.com> Signed-off-by: Dina Berry (MSFT) --- testrunner/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testrunner/package.json b/testrunner/package.json index 00922cb5..915465eb 100644 --- a/testrunner/package.json +++ b/testrunner/package.json @@ -5,7 +5,7 @@ "type": "module", "scripts": { "build": "rm -rf dist && tsc", - "test": "npm run build && NODE_DEBUG_NATIVE=node:test NODE_DEBUG=node:test node --test --experimental-test-coverage --experimental-test-module-mocks", + "test": "node --experimental-strip-types --test --experimental-test-coverage --experimental-test-module-mocks", "test:dev": "npm run build && node --watch --test test/ " }, "directories": { From 666277292131962218db6a2683f9e82aed73318e Mon Sep 17 00:00:00 2001 From: Dina Berry Date: Thu, 13 Mar 2025 21:46:33 +0000 Subject: [PATCH 12/27] 2 separate folders --- .../README.md | 0 .../package-lock.json | 0 .../package.json | 4 +- .../src/app/data/connect-to-cosmos.ts | 39 +++++++++++++++++++ .../src/app}/data/fake-data.ts | 2 +- .../src/app}/data/model.ts | 15 +++---- testrunner-typescript/src/app/data/verify.ts | 9 +++++ testrunner-typescript/src/app/index.ts | 17 ++++++++ .../src/app}/lib/insert.test.ts | 34 ++++++++-------- .../src/app}/lib/insert.ts | 14 +++---- .../test}/basics/01-spies.test.ts | 0 .../test}/basics/02-stubs.test.ts | 0 .../test/in-mem-db}/fake-in-mem-db.test.ts | 0 .../boilerplate-with-mock.test-2.ts | 6 +-- .../boilerplate-with-mock.test.ts | 0 .../test-boilerplate/boilerplate.test.ts | 0 .../tsconfig.json | 2 + .../mock-function/data/connect-to-cosmos.ts | 36 ----------------- testrunner/src/mock-function/data/verify.ts | 7 ---- testrunner/src/mock-function/index.ts | 17 -------- 20 files changed, 104 insertions(+), 98 deletions(-) rename {testrunner => testrunner-typescript}/README.md (100%) rename {testrunner => testrunner-typescript}/package-lock.json (100%) rename {testrunner => testrunner-typescript}/package.json (66%) create mode 100644 testrunner-typescript/src/app/data/connect-to-cosmos.ts rename {testrunner/src/mock-function => testrunner-typescript/src/app}/data/fake-data.ts (92%) rename {testrunner/src/mock-function => testrunner-typescript/src/app}/data/model.ts (99%) create mode 100644 testrunner-typescript/src/app/data/verify.ts create mode 100644 testrunner-typescript/src/app/index.ts rename {testrunner/src/mock-function => testrunner-typescript/src/app}/lib/insert.test.ts (84%) rename {testrunner/src/mock-function => testrunner-typescript/src/app}/lib/insert.ts (78%) rename {testrunner/src => testrunner-typescript/test}/basics/01-spies.test.ts (100%) rename {testrunner/src => testrunner-typescript/test}/basics/02-stubs.test.ts (100%) rename {testrunner/src/fakes => testrunner-typescript/test/in-mem-db}/fake-in-mem-db.test.ts (100%) rename {testrunner/src => testrunner-typescript/test}/test-boilerplate/boilerplate-with-mock.test-2.ts (79%) rename {testrunner/src => testrunner-typescript/test}/test-boilerplate/boilerplate-with-mock.test.ts (100%) rename {testrunner/src => testrunner-typescript/test}/test-boilerplate/boilerplate.test.ts (100%) rename {testrunner => testrunner-typescript}/tsconfig.json (90%) delete mode 100644 testrunner/src/mock-function/data/connect-to-cosmos.ts delete mode 100644 testrunner/src/mock-function/data/verify.ts delete mode 100644 testrunner/src/mock-function/index.ts diff --git a/testrunner/README.md b/testrunner-typescript/README.md similarity index 100% rename from testrunner/README.md rename to testrunner-typescript/README.md diff --git a/testrunner/package-lock.json b/testrunner-typescript/package-lock.json similarity index 100% rename from testrunner/package-lock.json rename to testrunner-typescript/package-lock.json diff --git a/testrunner/package.json b/testrunner-typescript/package.json similarity index 66% rename from testrunner/package.json rename to testrunner-typescript/package.json index 915465eb..5ef567f1 100644 --- a/testrunner/package.json +++ b/testrunner-typescript/package.json @@ -1,11 +1,11 @@ { - "name": "testrunner", + "name": "testrunner-native", "version": "1.0.0", "main": "index.js", "type": "module", "scripts": { "build": "rm -rf dist && tsc", - "test": "node --experimental-strip-types --test --experimental-test-coverage --experimental-test-module-mocks", + "test": "node --test --experimental-test-coverage --experimental-test-module-mocks", "test:dev": "npm run build && node --watch --test test/ " }, "directories": { diff --git a/testrunner-typescript/src/app/data/connect-to-cosmos.ts b/testrunner-typescript/src/app/data/connect-to-cosmos.ts new file mode 100644 index 00000000..bd0b9566 --- /dev/null +++ b/testrunner-typescript/src/app/data/connect-to-cosmos.ts @@ -0,0 +1,39 @@ +// connect-to-cosmos.ts + +import { Container, CosmosClient } from '@azure/cosmos'; +import { DefaultAzureCredential } from '@azure/identity'; +import 'dotenv/config'; +import { v4 as uuidv4 } from 'uuid'; + +export { Container }; + +export default class CosmosConnector { + static connectToCosmosWithoutKey(): CosmosClient { + const endpoint = process.env.COSMOS_DB_ENDPOINT!; + const credential = new DefaultAzureCredential(); + const client = new CosmosClient({ endpoint, aadCredentials: credential }); + return client; + } + + static async connectToContainer(): Promise { + const client = CosmosConnector.connectToCosmosWithoutKey(); + const databaseName = process.env.COSMOS_DATABASE_NAME!; + const containerName = process.env.COSMOS_CONTAINER_NAME!; + + // Ensure the database exists + const { database } = await client.databases.createIfNotExists({ + id: databaseName, + }); + + // Ensure the container exists + const { container } = await database.containers.createIfNotExists({ + id: containerName, + }); + + return container; + } + + static getUniqueId(): string { + return uuidv4(); + } +} diff --git a/testrunner/src/mock-function/data/fake-data.ts b/testrunner-typescript/src/app/data/fake-data.ts similarity index 92% rename from testrunner/src/mock-function/data/fake-data.ts rename to testrunner-typescript/src/app/data/fake-data.ts index 326912df..6deaa413 100644 --- a/testrunner/src/mock-function/data/fake-data.ts +++ b/testrunner-typescript/src/app/data/fake-data.ts @@ -1,6 +1,6 @@ import { v4 as uuidv4 } from 'uuid'; import { faker } from '@faker-js/faker'; -import { DbDocument, RawInput } from './model'; +import { DbDocument, RawInput } from './model.ts'; function createFixture(): T { const result = { diff --git a/testrunner/src/mock-function/data/model.ts b/testrunner-typescript/src/app/data/model.ts similarity index 99% rename from testrunner/src/mock-function/data/model.ts rename to testrunner-typescript/src/app/data/model.ts index cc747b54..6fd4ba38 100644 --- a/testrunner/src/mock-function/data/model.ts +++ b/testrunner-typescript/src/app/data/model.ts @@ -3,28 +3,25 @@ export interface DbDocument { id: string; name: string; } - export interface DbError { message: string; code: number; } -export function isDbError(error: any): error is DbError { - return 'message' in error && 'code' in error; -} - export interface VerificationErrors { message: string; } -export function isVerificationErrors(error: any): error is VerificationErrors { - return 'message' in error; -} - export interface RawInput { id: string; first: string; last: string; } +export function isDbError(error: any): error is DbError { + return 'message' in error && 'code' in error; +} +export function isVerificationErrors(error: any): error is VerificationErrors { + return 'message' in error; +} export function validateRawInput(input: any): string[] { const errors: string[] = []; diff --git a/testrunner-typescript/src/app/data/verify.ts b/testrunner-typescript/src/app/data/verify.ts new file mode 100644 index 00000000..1d507135 --- /dev/null +++ b/testrunner-typescript/src/app/data/verify.ts @@ -0,0 +1,9 @@ +// input-verified.ts +import { validateRawInput } from './model.ts'; + +export default class Verfiy { + static inputVerified(doc: any): boolean { + const result = validateRawInput(doc); + return result.length === 0; + } +} diff --git a/testrunner-typescript/src/app/index.ts b/testrunner-typescript/src/app/index.ts new file mode 100644 index 00000000..b75a8a00 --- /dev/null +++ b/testrunner-typescript/src/app/index.ts @@ -0,0 +1,17 @@ +import CosmosConnector from './data/connect-to-cosmos.ts'; +import { createTestInput } from './data/fake-data.ts'; +import type { DbDocument, DbError, VerificationErrors } from './data/model.ts'; +import { insertDocument } from './lib/insert.ts'; + +async function main(): Promise { + const container = await CosmosConnector.connectToContainer(); + const input = createTestInput(); + return await insertDocument(container, input); +} + +main() + .then((doc) => console.log(doc)) + .catch((error) => { + console.error(error); + process.exit(1); + }); diff --git a/testrunner/src/mock-function/lib/insert.test.ts b/testrunner-typescript/src/app/lib/insert.test.ts similarity index 84% rename from testrunner/src/mock-function/lib/insert.test.ts rename to testrunner-typescript/src/app/lib/insert.test.ts index 31c884ef..cc28563a 100644 --- a/testrunner/src/mock-function/lib/insert.test.ts +++ b/testrunner-typescript/src/app/lib/insert.test.ts @@ -1,19 +1,21 @@ // insertDocument.test.ts -import { describe, it, beforeEach, mock } from 'node:test'; import assert from 'node:assert'; -import { Container } from '../data/connect-to-cosmos'; -import { createTestInputAndResult } from '../data/fake-data'; -import { +import { beforeEach, describe, it, mock } from 'node:test'; +import { Container } from '../data/connect-to-cosmos.ts'; +import { createTestInputAndResult } from '../data/fake-data.ts'; +import type { DbDocument, DbError, + RawInput +} from '../data/model.ts'; +import { isDbError, - isVerificationErrors, - RawInput, -} from '../data/model'; + isVerificationErrors +} from '../data/model.ts'; // Instead of jest.mock, import the whole module to override functions as needed. -import * as verifyModule from '../data/verify'; -import * as dbModule from '../data/connect-to-cosmos'; -import { insertDocument } from './insert'; +import CosmosConnector from '../data/connect-to-cosmos.ts'; +import Verify from '../data/verify.ts'; +import { insertDocument } from './insert.ts'; // --- Test suite for insertDocument --- describe('Insert into db', () => { @@ -24,7 +26,7 @@ describe('Insert into db', () => { mock.restoreAll() }) - test("Success", () => { + it("Success", () => { it('should insert document successfully', async () => { // Arrange: override inputVerified to return true. const { input, result }: { input: RawInput; result: Partial } = createTestInputAndResult(); @@ -37,7 +39,7 @@ describe('Insert into db', () => { }, } as unknown as Container; - const mVerify = mock.method(verifyModule, "inputVerified").mock; + const mVerify = mock.method(Verify, "inputVerified").mock; mVerify.mockImplementation(() => true); const mContainerCreate = mock.method(fakeContainer.items as any, "create").mock; @@ -60,7 +62,7 @@ describe('Insert into db', () => { }); }); - test("Failure", () => { + it("Failure", () => { it('should return verification error if input is not verified', async () => { const fakeContainer = { @@ -71,10 +73,10 @@ describe('Insert into db', () => { }, } as unknown as Container; - const mVerify = mock.method(verifyModule, "inputVerified").mock; + const mVerify = mock.method(Verify, "inputVerified").mock; mVerify.mockImplementation(() => false); - const mGetUniqueId = mock.method(dbModule, "getUniqueId").mock; + const mGetUniqueId = mock.method(CosmosConnector, "getUniqueId").mock; mGetUniqueId.mockImplementation(() => 'unique-id'); const mContainerCreate = mock.method(fakeContainer.items, "create").mock; @@ -111,7 +113,7 @@ describe('Insert into db', () => { }, } as unknown as Container; - const mVerify = mock.method(verifyModule, "inputVerified").mock; + const mVerify = mock.method(Verify, "inputVerified").mock; mVerify.mockImplementation(() => true); const mContainerCreate = mock.method(fakeContainer.items, "create").mock; diff --git a/testrunner/src/mock-function/lib/insert.ts b/testrunner-typescript/src/app/lib/insert.ts similarity index 78% rename from testrunner/src/mock-function/lib/insert.ts rename to testrunner-typescript/src/app/lib/insert.ts index 8b9f8871..4549bc07 100644 --- a/testrunner/src/mock-function/lib/insert.ts +++ b/testrunner-typescript/src/app/lib/insert.ts @@ -1,18 +1,18 @@ // insertDocument.ts -import { Container } from '../data/connect-to-cosmos'; -import { +import { Container } from '../data/connect-to-cosmos.ts'; +import type { DbDocument, - DbError, + DbError, RawInput, - VerificationErrors, -} from '../data/model'; -import { inputVerified } from '../data/verify'; + VerificationErrors, +} from '../data/model.ts'; +import Verify from '../data/verify.ts'; export async function insertDocument( container: Container, doc: RawInput, ): Promise { - const isVerified: boolean = inputVerified(doc); + const isVerified: boolean = Verify.inputVerified(doc); if (!isVerified) { return { message: 'Verification failed' } as VerificationErrors; diff --git a/testrunner/src/basics/01-spies.test.ts b/testrunner-typescript/test/basics/01-spies.test.ts similarity index 100% rename from testrunner/src/basics/01-spies.test.ts rename to testrunner-typescript/test/basics/01-spies.test.ts diff --git a/testrunner/src/basics/02-stubs.test.ts b/testrunner-typescript/test/basics/02-stubs.test.ts similarity index 100% rename from testrunner/src/basics/02-stubs.test.ts rename to testrunner-typescript/test/basics/02-stubs.test.ts diff --git a/testrunner/src/fakes/fake-in-mem-db.test.ts b/testrunner-typescript/test/in-mem-db/fake-in-mem-db.test.ts similarity index 100% rename from testrunner/src/fakes/fake-in-mem-db.test.ts rename to testrunner-typescript/test/in-mem-db/fake-in-mem-db.test.ts diff --git a/testrunner/src/test-boilerplate/boilerplate-with-mock.test-2.ts b/testrunner-typescript/test/test-boilerplate/boilerplate-with-mock.test-2.ts similarity index 79% rename from testrunner/src/test-boilerplate/boilerplate-with-mock.test-2.ts rename to testrunner-typescript/test/test-boilerplate/boilerplate-with-mock.test-2.ts index ded260d2..3d7614fb 100644 --- a/testrunner/src/test-boilerplate/boilerplate-with-mock.test-2.ts +++ b/testrunner-typescript/test/test-boilerplate/boilerplate-with-mock.test-2.ts @@ -7,7 +7,7 @@ import { } from 'node:test' import assert from 'node:assert'; -import * as MyService from '../mock-function/data/connect-to-cosmos' +import CosmosConnector from '../../src/app/data/connect-to-cosmos.ts'; describe('boilerplate with mock 2', () => { beforeEach(() =>{ @@ -20,7 +20,7 @@ describe('boilerplate with mock 2', () => { it('should if ', async () => { - const m = mock.method(MyService, "getUniqueId"); + const m = mock.method(CosmosConnector, "getUniqueId"); m.mock.mockImplementation(() => { // Replace the original implementation with a mock result @@ -29,7 +29,7 @@ describe('boilerplate with mock 2', () => { }); - const result = await MyService.getUniqueId(); + const result = await CosmosConnector.getUniqueId(); assert.strictEqual(result, '12345678-1234-1234-1234-123456789012' ) diff --git a/testrunner/src/test-boilerplate/boilerplate-with-mock.test.ts b/testrunner-typescript/test/test-boilerplate/boilerplate-with-mock.test.ts similarity index 100% rename from testrunner/src/test-boilerplate/boilerplate-with-mock.test.ts rename to testrunner-typescript/test/test-boilerplate/boilerplate-with-mock.test.ts diff --git a/testrunner/src/test-boilerplate/boilerplate.test.ts b/testrunner-typescript/test/test-boilerplate/boilerplate.test.ts similarity index 100% rename from testrunner/src/test-boilerplate/boilerplate.test.ts rename to testrunner-typescript/test/test-boilerplate/boilerplate.test.ts diff --git a/testrunner/tsconfig.json b/testrunner-typescript/tsconfig.json similarity index 90% rename from testrunner/tsconfig.json rename to testrunner-typescript/tsconfig.json index 4aeff363..af542786 100644 --- a/testrunner/tsconfig.json +++ b/testrunner-typescript/tsconfig.json @@ -9,6 +9,8 @@ "outDir": "./dist", "rootDir": "./", "erasableSyntaxOnly": true, + "allowImportingTsExtensions": true, + "noEmit": true }, "include": [ "src", diff --git a/testrunner/src/mock-function/data/connect-to-cosmos.ts b/testrunner/src/mock-function/data/connect-to-cosmos.ts deleted file mode 100644 index bc9d9108..00000000 --- a/testrunner/src/mock-function/data/connect-to-cosmos.ts +++ /dev/null @@ -1,36 +0,0 @@ -// connect-to-cosmos.ts - -import { Container, CosmosClient } from '@azure/cosmos'; -import { DefaultAzureCredential } from '@azure/identity'; -import 'dotenv/config'; -import { v4 as uuidv4 } from 'uuid'; - -export { Container }; - -export function connectToCosmosWithoutKey() { - const endpoint = process.env.COSMOS_DB_ENDPOINT!; - const credential = new DefaultAzureCredential(); - - const client = new CosmosClient({ endpoint, aadCredentials: credential }); - return client; -} -export async function connectToContainer(): Promise { - const client = connectToCosmosWithoutKey(); - const databaseName = process.env.COSMOS_DATABASE_NAME; - const containerName = process.env.COSMOS_CONTAINER_NAME; - - // Ensure the database exists - const { database } = await client.databases.createIfNotExists({ - id: databaseName, - }); - - // Ensure the container exists - const { container } = await database.containers.createIfNotExists({ - id: containerName, - }); - - return container; -} -export function getUniqueId(): string { - return uuidv4(); -} diff --git a/testrunner/src/mock-function/data/verify.ts b/testrunner/src/mock-function/data/verify.ts deleted file mode 100644 index d71969d5..00000000 --- a/testrunner/src/mock-function/data/verify.ts +++ /dev/null @@ -1,7 +0,0 @@ -// input-verified.ts -import { validateRawInput } from './model'; - -export function inputVerified(doc: any): boolean { - const result = validateRawInput(doc); - return result.length === 0 ? true : false; -} diff --git a/testrunner/src/mock-function/index.ts b/testrunner/src/mock-function/index.ts deleted file mode 100644 index 1fb12681..00000000 --- a/testrunner/src/mock-function/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { connectToContainer } from './data/connect-to-cosmos'; -import { createTestInput } from './data/fake-data'; -import { DbDocument, DbError, VerificationErrors } from './data/model'; -import { insertDocument } from './lib/insert'; - -async function main(): Promise { - const container = await connectToContainer(); - const input = createTestInput(); - return await insertDocument(container, input); -} - -main() - .then((doc) => console.log(doc)) - .catch((error) => { - console.error(error); - process.exit(1); - }); From 51b06f93b99ea58709ad547a31b938d17a441784 Mon Sep 17 00:00:00 2001 From: Dina Berry Date: Thu, 13 Mar 2025 21:51:45 +0000 Subject: [PATCH 13/27] revert to see suggestions again --- testrunner-typescript/package.json | 5 +- .../src/app/data/fake-data.ts | 2 +- testrunner-typescript/src/app/data/verify.ts | 2 +- testrunner-typescript/src/app/index.ts | 8 +- testrunner-typescript/src/app/lib/insert.ts | 6 +- .../test-app}/boilerplate-with-mock.test-2.ts | 2 +- .../src/{app/lib => test-app}/insert.test.ts | 16 +- .../test-basics}/01-spies.test.ts | 0 .../test-basics}/02-stubs.test.ts | 0 .../boilerplate-with-mock.test.ts | 0 .../test-boilerplate/boilerplate.test.ts | 0 .../test-fakes}/fake-in-mem-db.test.ts | 0 testrunner-typescript/tsconfig.json | 2 - testrunner/README.md | 29 ++++ testrunner/package-lock.json | 30 ++++ testrunner/package.json | 16 ++ testrunner/src/basics/01-spies.test.ts | 28 ++++ testrunner/src/basics/02-stubs.test.ts | 119 +++++++++++++++ testrunner/src/fakes/fake-in-mem-db.test.ts | 74 +++++++++ .../mock-function/data/connect-to-cosmos.ts | 36 +++++ .../src/mock-function/data/fake-data.ts | 28 ++++ testrunner/src/mock-function/data/model.ts | 42 +++++ testrunner/src/mock-function/data/verify.ts | 7 + testrunner/src/mock-function/index.ts | 17 +++ .../src/mock-function/lib/insert.test.ts | 143 ++++++++++++++++++ testrunner/src/mock-function/lib/insert.ts | 41 +++++ .../boilerplate-with-mock.test-2.ts | 38 +++++ .../boilerplate-with-mock.test.ts | 42 +++++ .../src/test-boilerplate/boilerplate.test.ts | 28 ++++ testrunner/tsconfig.json | 19 +++ 30 files changed, 757 insertions(+), 23 deletions(-) rename testrunner-typescript/{test/test-boilerplate => src/test-app}/boilerplate-with-mock.test-2.ts (92%) rename testrunner-typescript/src/{app/lib => test-app}/insert.test.ts (91%) rename testrunner-typescript/{test/basics => src/test-basics}/01-spies.test.ts (100%) rename testrunner-typescript/{test/basics => src/test-basics}/02-stubs.test.ts (100%) rename testrunner-typescript/{test => src}/test-boilerplate/boilerplate-with-mock.test.ts (100%) rename testrunner-typescript/{test => src}/test-boilerplate/boilerplate.test.ts (100%) rename testrunner-typescript/{test/in-mem-db => src/test-fakes}/fake-in-mem-db.test.ts (100%) create mode 100644 testrunner/README.md create mode 100644 testrunner/package-lock.json create mode 100644 testrunner/package.json create mode 100644 testrunner/src/basics/01-spies.test.ts create mode 100644 testrunner/src/basics/02-stubs.test.ts create mode 100644 testrunner/src/fakes/fake-in-mem-db.test.ts create mode 100644 testrunner/src/mock-function/data/connect-to-cosmos.ts create mode 100644 testrunner/src/mock-function/data/fake-data.ts create mode 100644 testrunner/src/mock-function/data/model.ts create mode 100644 testrunner/src/mock-function/data/verify.ts create mode 100644 testrunner/src/mock-function/index.ts create mode 100644 testrunner/src/mock-function/lib/insert.test.ts create mode 100644 testrunner/src/mock-function/lib/insert.ts create mode 100644 testrunner/src/test-boilerplate/boilerplate-with-mock.test-2.ts create mode 100644 testrunner/src/test-boilerplate/boilerplate-with-mock.test.ts create mode 100644 testrunner/src/test-boilerplate/boilerplate.test.ts create mode 100644 testrunner/tsconfig.json diff --git a/testrunner-typescript/package.json b/testrunner-typescript/package.json index 5ef567f1..69af8966 100644 --- a/testrunner-typescript/package.json +++ b/testrunner-typescript/package.json @@ -1,11 +1,10 @@ { - "name": "testrunner-native", + "name": "testrunner", "version": "1.0.0", "main": "index.js", - "type": "module", "scripts": { "build": "rm -rf dist && tsc", - "test": "node --test --experimental-test-coverage --experimental-test-module-mocks", + "test": "npm run build && node --test --no-experimental-strip-types --experimental-test-coverage --experimental-test-module-mocks", "test:dev": "npm run build && node --watch --test test/ " }, "directories": { diff --git a/testrunner-typescript/src/app/data/fake-data.ts b/testrunner-typescript/src/app/data/fake-data.ts index 6deaa413..326912df 100644 --- a/testrunner-typescript/src/app/data/fake-data.ts +++ b/testrunner-typescript/src/app/data/fake-data.ts @@ -1,6 +1,6 @@ import { v4 as uuidv4 } from 'uuid'; import { faker } from '@faker-js/faker'; -import { DbDocument, RawInput } from './model.ts'; +import { DbDocument, RawInput } from './model'; function createFixture(): T { const result = { diff --git a/testrunner-typescript/src/app/data/verify.ts b/testrunner-typescript/src/app/data/verify.ts index 1d507135..3cb435e6 100644 --- a/testrunner-typescript/src/app/data/verify.ts +++ b/testrunner-typescript/src/app/data/verify.ts @@ -1,5 +1,5 @@ // input-verified.ts -import { validateRawInput } from './model.ts'; +import { validateRawInput } from './model'; export default class Verfiy { static inputVerified(doc: any): boolean { diff --git a/testrunner-typescript/src/app/index.ts b/testrunner-typescript/src/app/index.ts index b75a8a00..3c81fe0a 100644 --- a/testrunner-typescript/src/app/index.ts +++ b/testrunner-typescript/src/app/index.ts @@ -1,7 +1,7 @@ -import CosmosConnector from './data/connect-to-cosmos.ts'; -import { createTestInput } from './data/fake-data.ts'; -import type { DbDocument, DbError, VerificationErrors } from './data/model.ts'; -import { insertDocument } from './lib/insert.ts'; +import CosmosConnector from './data/connect-to-cosmos'; +import { createTestInput } from './data/fake-data'; +import type { DbDocument, DbError, VerificationErrors } from './data/model'; +import { insertDocument } from './lib/insert'; async function main(): Promise { const container = await CosmosConnector.connectToContainer(); diff --git a/testrunner-typescript/src/app/lib/insert.ts b/testrunner-typescript/src/app/lib/insert.ts index 4549bc07..f5d99b10 100644 --- a/testrunner-typescript/src/app/lib/insert.ts +++ b/testrunner-typescript/src/app/lib/insert.ts @@ -1,12 +1,12 @@ // insertDocument.ts -import { Container } from '../data/connect-to-cosmos.ts'; +import { Container } from '../data/connect-to-cosmos'; import type { DbDocument, DbError, RawInput, VerificationErrors, -} from '../data/model.ts'; -import Verify from '../data/verify.ts'; +} from '../data/model'; +import Verify from '../data/verify'; export async function insertDocument( container: Container, diff --git a/testrunner-typescript/test/test-boilerplate/boilerplate-with-mock.test-2.ts b/testrunner-typescript/src/test-app/boilerplate-with-mock.test-2.ts similarity index 92% rename from testrunner-typescript/test/test-boilerplate/boilerplate-with-mock.test-2.ts rename to testrunner-typescript/src/test-app/boilerplate-with-mock.test-2.ts index 3d7614fb..a29022a3 100644 --- a/testrunner-typescript/test/test-boilerplate/boilerplate-with-mock.test-2.ts +++ b/testrunner-typescript/src/test-app/boilerplate-with-mock.test-2.ts @@ -7,7 +7,7 @@ import { } from 'node:test' import assert from 'node:assert'; -import CosmosConnector from '../../src/app/data/connect-to-cosmos.ts'; +import CosmosConnector from '../app/data/connect-to-cosmos'; describe('boilerplate with mock 2', () => { beforeEach(() =>{ diff --git a/testrunner-typescript/src/app/lib/insert.test.ts b/testrunner-typescript/src/test-app/insert.test.ts similarity index 91% rename from testrunner-typescript/src/app/lib/insert.test.ts rename to testrunner-typescript/src/test-app/insert.test.ts index cc28563a..0b4a0684 100644 --- a/testrunner-typescript/src/app/lib/insert.test.ts +++ b/testrunner-typescript/src/test-app/insert.test.ts @@ -1,21 +1,21 @@ // insertDocument.test.ts +import { describe, it, beforeEach, mock } from 'node:test'; import assert from 'node:assert'; -import { beforeEach, describe, it, mock } from 'node:test'; -import { Container } from '../data/connect-to-cosmos.ts'; -import { createTestInputAndResult } from '../data/fake-data.ts'; +import { Container } from '../app/data/connect-to-cosmos'; +import { createTestInputAndResult } from '../app/data/fake-data'; import type { DbDocument, DbError, RawInput -} from '../data/model.ts'; +} from '../app/data/model'; import { isDbError, isVerificationErrors -} from '../data/model.ts'; +} from '../app/data/model'; // Instead of jest.mock, import the whole module to override functions as needed. -import CosmosConnector from '../data/connect-to-cosmos.ts'; -import Verify from '../data/verify.ts'; -import { insertDocument } from './insert.ts'; +import Verify from '../app/data/verify'; +import CosmosConnector from '../app/data/connect-to-cosmos'; +import { insertDocument } from '../app/lib/insert'; // --- Test suite for insertDocument --- describe('Insert into db', () => { diff --git a/testrunner-typescript/test/basics/01-spies.test.ts b/testrunner-typescript/src/test-basics/01-spies.test.ts similarity index 100% rename from testrunner-typescript/test/basics/01-spies.test.ts rename to testrunner-typescript/src/test-basics/01-spies.test.ts diff --git a/testrunner-typescript/test/basics/02-stubs.test.ts b/testrunner-typescript/src/test-basics/02-stubs.test.ts similarity index 100% rename from testrunner-typescript/test/basics/02-stubs.test.ts rename to testrunner-typescript/src/test-basics/02-stubs.test.ts diff --git a/testrunner-typescript/test/test-boilerplate/boilerplate-with-mock.test.ts b/testrunner-typescript/src/test-boilerplate/boilerplate-with-mock.test.ts similarity index 100% rename from testrunner-typescript/test/test-boilerplate/boilerplate-with-mock.test.ts rename to testrunner-typescript/src/test-boilerplate/boilerplate-with-mock.test.ts diff --git a/testrunner-typescript/test/test-boilerplate/boilerplate.test.ts b/testrunner-typescript/src/test-boilerplate/boilerplate.test.ts similarity index 100% rename from testrunner-typescript/test/test-boilerplate/boilerplate.test.ts rename to testrunner-typescript/src/test-boilerplate/boilerplate.test.ts diff --git a/testrunner-typescript/test/in-mem-db/fake-in-mem-db.test.ts b/testrunner-typescript/src/test-fakes/fake-in-mem-db.test.ts similarity index 100% rename from testrunner-typescript/test/in-mem-db/fake-in-mem-db.test.ts rename to testrunner-typescript/src/test-fakes/fake-in-mem-db.test.ts diff --git a/testrunner-typescript/tsconfig.json b/testrunner-typescript/tsconfig.json index af542786..4aeff363 100644 --- a/testrunner-typescript/tsconfig.json +++ b/testrunner-typescript/tsconfig.json @@ -9,8 +9,6 @@ "outDir": "./dist", "rootDir": "./", "erasableSyntaxOnly": true, - "allowImportingTsExtensions": true, - "noEmit": true }, "include": [ "src", diff --git a/testrunner/README.md b/testrunner/README.md new file mode 100644 index 00000000..376bbee7 --- /dev/null +++ b/testrunner/README.md @@ -0,0 +1,29 @@ +# Unit testing for the Azure SDK for JavaScript + +This subfolder is the source code for the [TBD article](). The purpose is to demonstrate unit test mocks for the Azure SDK for JavaScript. These specific examples use Azure Cosmos DB. + +## To run the test + +1. `npm install` +2. `npm run build` +3. `npm test` + + ```console + > unit-testing@1.0.0 test + > jest dist + + PASS dist/fakes/fake-in-mem-db.spec.js + PASS dist/mock-function/lib/insert.spec.js + + Test Suites: 2 passed, 2 total + Tests: 4 passed, 4 total + Snapshots: 0 total + Time: 4.247 s, estimated 5 s + Ran all test suites matching /dist/i. + ``` + +## Related content + +* [Passwordless connections for Azure services](https://learn.microsoft.com/azure/developer/intro/passwordless-overview) +* [Cosmos DB keyless access to the service](https://learn.microsoft.com/azure/cosmos-db/role-based-access-control) +* [Node.js Test Runner](https://nodejs.org/docs/latest/api/test.html#test-runner) \ No newline at end of file diff --git a/testrunner/package-lock.json b/testrunner/package-lock.json new file mode 100644 index 00000000..613ce82c --- /dev/null +++ b/testrunner/package-lock.json @@ -0,0 +1,30 @@ +{ + "name": "testrunner", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "testrunner", + "version": "1.0.0", + "dependencies": { + "@types/node": "^22.13.10" + } + }, + "node_modules/@types/node": { + "version": "22.13.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.10.tgz", + "integrity": "sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "license": "MIT" + } + } +} diff --git a/testrunner/package.json b/testrunner/package.json new file mode 100644 index 00000000..ea96876c --- /dev/null +++ b/testrunner/package.json @@ -0,0 +1,16 @@ +{ + "name": "testrunner", + "version": "1.0.0", + "main": "index.js", + "scripts": { + "build": "rm -rf dist && tsc", + "test": "npm run build && NODE_DEBUG_NATIVE=node:test NODE_DEBUG=node:test node --test --experimental-test-coverage --experimental-test-module-mocks", + "test:dev": "npm run build && node --watch --test test/ " + }, + "directories": { + "test": "test" + }, + "dependencies": { + "@types/node": "^22.13.10" + } +} diff --git a/testrunner/src/basics/01-spies.test.ts b/testrunner/src/basics/01-spies.test.ts new file mode 100644 index 00000000..8bcbea13 --- /dev/null +++ b/testrunner/src/basics/01-spies.test.ts @@ -0,0 +1,28 @@ +import { + describe, + it, + mock +} from 'node:test'; +import assert from 'node:assert'; + + +function run({fn, times}){ + for(let i = 0; i < times; i++){ + fn({current: i * 5}); + } +} + +describe('Spies', () => { + it('should verify calls in a mock', () => { + const spy = mock.fn(); + run({fn: spy, times: 3}); + + assert.strictEqual(spy.mock.callCount(), 3); + const calls = spy.mock.calls; + + assert.deepStrictEqual(calls[0].arguments[0], {current: 0}); + assert.deepStrictEqual(calls[1].arguments[0], {current: 5}); + assert.deepStrictEqual(calls[2].arguments[0], {current: 10}); + + }); +}); \ No newline at end of file diff --git a/testrunner/src/basics/02-stubs.test.ts b/testrunner/src/basics/02-stubs.test.ts new file mode 100644 index 00000000..207abfe4 --- /dev/null +++ b/testrunner/src/basics/02-stubs.test.ts @@ -0,0 +1,119 @@ +import { + describe, + it, + beforeEach, + mock +} from 'node:test' +import assert from 'node:assert' + +class Service { + static async getTalks({ skip, limit }) { + const items = await fetch('https://tml-api.herokuapp.com/graphql', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + query: ` + { + getTalks (skip: ${skip}, limit: ${limit}) { + totalCount, + talks { + _id + title + } + } + } + ` + }) + }) + return (await items.json()).data.getTalks.talks + } +} + +function mapResponse(data) { + return data + .map(({ _id, title }, index) => `[${index}] id: ${_id}, title: ${title}`) + .join('\n') +} + +async function run({ skip = 0, limit = 10 }) { + const talks = mapResponse(await Service.getTalks({ skip, limit })) + return talks +} + +describe('Stub Test Suite', () => { + beforeEach(() => mock.restoreAll()) + + it('should stub APIs', async () => { + const m = mock.method( + Service, + "getTalks", + ).mock; + m.mockImplementation(async () => [ + { + _id: '63865750c839dbaacd8116e1', + title: 'The Journey About How I Fixed a Bug in the Node.js Core That Affected Thousands of Packages' + } + ]) + + const result = await run({ limit: 1 }) + const expected = `[0] id: 63865750c839dbaacd8116e1, title: The Journey About How I Fixed a Bug in the Node.js Core That Affected Thousands of Packages` + + assert.deepStrictEqual(m.callCount(), 1) + const calls = m.calls + + assert.deepStrictEqual(calls[0].arguments[0], { skip: 0, limit: 1 }) + assert.strictEqual(result, expected) + }) + + it('should stub different values for API calls', async () => { + const m = mock.method( + Service, + "getTalks", + ).mock + + m.mockImplementationOnce(async () => [ + { + _id: '63865750c839dbaacd8116e1', + title: 'The Journey About How I Fixed a Bug in the Node.js Core That Affected Thousands of Packages' + } + ], 0) + + m.mockImplementationOnce(async () => [ + { + _id: '01', + title: 'Mock 01' + } + ], 1) + + m.mockImplementationOnce(async () => [ + { + _id: '02', + title: 'Mock 02' + } + ], 2) + + { + const result = await run({ skip: 0, limit: 1 }) + const expected = `[0] id: 63865750c839dbaacd8116e1, title: The Journey About How I Fixed a Bug in the Node.js Core That Affected Thousands of Packages` + assert.strictEqual(result, expected) + } + { + const result = await run({ skip: 1, limit: 1 }) + const expected = `[0] id: 01, title: Mock 01` + assert.strictEqual(result, expected) + } + { + const result = await run({ skip: 2, limit: 1 }) + const expected = `[0] id: 02, title: Mock 02` + assert.strictEqual(result, expected) + } + + const calls = m.calls + assert.strictEqual(m.callCount(), 3) + assert.deepStrictEqual(calls[0].arguments[0], { skip: 0, limit: 1 }) + assert.deepStrictEqual(calls[1].arguments[0], { skip: 1, limit: 1 }) + assert.deepStrictEqual(calls[2].arguments[0], { skip: 2, limit: 1 }) + }) +}) \ No newline at end of file diff --git a/testrunner/src/fakes/fake-in-mem-db.test.ts b/testrunner/src/fakes/fake-in-mem-db.test.ts new file mode 100644 index 00000000..103000d6 --- /dev/null +++ b/testrunner/src/fakes/fake-in-mem-db.test.ts @@ -0,0 +1,74 @@ +// fake-in-mem-db.spec.ts +import { describe, it, beforeEach, afterEach } from 'node:test'; +import assert from 'node:assert'; + +class FakeDatabase { + private data: Record; + + constructor() { + this.data = {}; + } + + save(key: string, value: any): void { + this.data[key] = value; + } + + get(key: string): any { + return this.data[key]; + } +} + +// Function to test +function someTestFunction(db: FakeDatabase, key: string, value: any): any { + db.save(key, value); + return db.get(key); +} + +describe('In-Mem DB', () => { + let fakeDb: FakeDatabase; + let testKey: string; + let testValue: any; + let originalSave: (key: string, value: any) => void; + let saveSpyCallCount: number; + let saveSpyArgs: Array<[string, any]>; + + beforeEach(() => { + fakeDb = new FakeDatabase(); + testKey = 'testKey'; + testValue = { + first: 'John', + last: 'Jones', + lastUpdated: new Date().toISOString(), + }; + + // Create a simple spy on the save method + originalSave = fakeDb.save.bind(fakeDb); + saveSpyCallCount = 0; + saveSpyArgs = []; + fakeDb.save = function (key: string, value: any): void { + saveSpyCallCount++; + saveSpyArgs.push([key, value]); + return originalSave(key, value); + }; + }); + + afterEach(() => { + // Restore original method if necessary + fakeDb.save = originalSave; + }); + + it('should save and return the correct value', () => { + // Call the function under test + const result = someTestFunction(fakeDb, testKey, testValue); + + // Verify state + assert.deepStrictEqual(result, testValue); + assert.strictEqual(result.first, 'John'); + assert.strictEqual(result.last, 'Jones'); + assert.strictEqual(result.lastUpdated, testValue.lastUpdated); + + // Verify behavior using our manual spy + assert.strictEqual(saveSpyCallCount, 1); + assert.deepStrictEqual(saveSpyArgs[0], [testKey, testValue]); + }); +}); \ No newline at end of file diff --git a/testrunner/src/mock-function/data/connect-to-cosmos.ts b/testrunner/src/mock-function/data/connect-to-cosmos.ts new file mode 100644 index 00000000..bc9d9108 --- /dev/null +++ b/testrunner/src/mock-function/data/connect-to-cosmos.ts @@ -0,0 +1,36 @@ +// connect-to-cosmos.ts + +import { Container, CosmosClient } from '@azure/cosmos'; +import { DefaultAzureCredential } from '@azure/identity'; +import 'dotenv/config'; +import { v4 as uuidv4 } from 'uuid'; + +export { Container }; + +export function connectToCosmosWithoutKey() { + const endpoint = process.env.COSMOS_DB_ENDPOINT!; + const credential = new DefaultAzureCredential(); + + const client = new CosmosClient({ endpoint, aadCredentials: credential }); + return client; +} +export async function connectToContainer(): Promise { + const client = connectToCosmosWithoutKey(); + const databaseName = process.env.COSMOS_DATABASE_NAME; + const containerName = process.env.COSMOS_CONTAINER_NAME; + + // Ensure the database exists + const { database } = await client.databases.createIfNotExists({ + id: databaseName, + }); + + // Ensure the container exists + const { container } = await database.containers.createIfNotExists({ + id: containerName, + }); + + return container; +} +export function getUniqueId(): string { + return uuidv4(); +} diff --git a/testrunner/src/mock-function/data/fake-data.ts b/testrunner/src/mock-function/data/fake-data.ts new file mode 100644 index 00000000..326912df --- /dev/null +++ b/testrunner/src/mock-function/data/fake-data.ts @@ -0,0 +1,28 @@ +import { v4 as uuidv4 } from 'uuid'; +import { faker } from '@faker-js/faker'; +import { DbDocument, RawInput } from './model'; + +function createFixture(): T { + const result = { + first: faker.person.firstName(), + last: faker.person.lastName(), + }; + return result as T; +} + +export function createTestInput(): RawInput { + const { first, last } = createFixture(); + return { id: uuidv4(), first, last }; +} + +export function createTestInputAndResult(): { + input: RawInput; + result: Partial; +} { + const input = createTestInput(); + const result = { + id: input.id, + name: `${input.first} ${input.last}`, + }; + return { input, result }; +} diff --git a/testrunner/src/mock-function/data/model.ts b/testrunner/src/mock-function/data/model.ts new file mode 100644 index 00000000..cc747b54 --- /dev/null +++ b/testrunner/src/mock-function/data/model.ts @@ -0,0 +1,42 @@ +// input-verified.ts +export interface DbDocument { + id: string; + name: string; +} + +export interface DbError { + message: string; + code: number; +} +export function isDbError(error: any): error is DbError { + return 'message' in error && 'code' in error; +} + +export interface VerificationErrors { + message: string; +} +export function isVerificationErrors(error: any): error is VerificationErrors { + return 'message' in error; +} + +export interface RawInput { + id: string; + first: string; + last: string; +} + +export function validateRawInput(input: any): string[] { + const errors: string[] = []; + + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + if (typeof input.first !== 'string' || input.first.trim().length === 0) { + errors.push('First name is required and must be a non-empty string'); + } + + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + if (typeof input.last !== 'string' || input.last.trim().length === 0) { + errors.push('Last name is required and must be a non-empty string'); + } + + return errors; +} diff --git a/testrunner/src/mock-function/data/verify.ts b/testrunner/src/mock-function/data/verify.ts new file mode 100644 index 00000000..d71969d5 --- /dev/null +++ b/testrunner/src/mock-function/data/verify.ts @@ -0,0 +1,7 @@ +// input-verified.ts +import { validateRawInput } from './model'; + +export function inputVerified(doc: any): boolean { + const result = validateRawInput(doc); + return result.length === 0 ? true : false; +} diff --git a/testrunner/src/mock-function/index.ts b/testrunner/src/mock-function/index.ts new file mode 100644 index 00000000..1fb12681 --- /dev/null +++ b/testrunner/src/mock-function/index.ts @@ -0,0 +1,17 @@ +import { connectToContainer } from './data/connect-to-cosmos'; +import { createTestInput } from './data/fake-data'; +import { DbDocument, DbError, VerificationErrors } from './data/model'; +import { insertDocument } from './lib/insert'; + +async function main(): Promise { + const container = await connectToContainer(); + const input = createTestInput(); + return await insertDocument(container, input); +} + +main() + .then((doc) => console.log(doc)) + .catch((error) => { + console.error(error); + process.exit(1); + }); diff --git a/testrunner/src/mock-function/lib/insert.test.ts b/testrunner/src/mock-function/lib/insert.test.ts new file mode 100644 index 00000000..31c884ef --- /dev/null +++ b/testrunner/src/mock-function/lib/insert.test.ts @@ -0,0 +1,143 @@ +// insertDocument.test.ts +import { describe, it, beforeEach, mock } from 'node:test'; +import assert from 'node:assert'; +import { Container } from '../data/connect-to-cosmos'; +import { createTestInputAndResult } from '../data/fake-data'; +import { + DbDocument, + DbError, + isDbError, + isVerificationErrors, + RawInput, +} from '../data/model'; +// Instead of jest.mock, import the whole module to override functions as needed. +import * as verifyModule from '../data/verify'; +import * as dbModule from '../data/connect-to-cosmos'; +import { insertDocument } from './insert'; + +// --- Test suite for insertDocument --- +describe('Insert into db', () => { + + // Set up a fresh container object before each test. + beforeEach(() => { + // Setup required before each test + mock.restoreAll() + }) + + test("Success", () => { + it('should insert document successfully', async () => { + // Arrange: override inputVerified to return true. + const { input, result }: { input: RawInput; result: Partial } = createTestInputAndResult(); + + const fakeContainer = { + items: { + create: async (doc: any) => { + return { resource: result }; + }, + }, + } as unknown as Container; + + const mVerify = mock.method(verifyModule, "inputVerified").mock; + mVerify.mockImplementation(() => true); + + const mContainerCreate = mock.method(fakeContainer.items as any, "create").mock; + mContainerCreate.mockImplementation(async (doc: any) => { + return { resource: result }; + }); + + // Act: + const receivedResult = await insertDocument(fakeContainer, input); + + // Assert - State verification: Ensure the result is as expected. + assert.deepStrictEqual(receivedResult, result); + + // Assert - Behavior verification: Ensure create was called once with correct arguments. + assert.strictEqual(mContainerCreate.callCount(), 1); + assert.deepStrictEqual(mContainerCreate.calls[0].arguments[0], { + id: input.id, + name: result.name, + }); + }); + }); + + test("Failure", () => { + it('should return verification error if input is not verified', async () => { + + const fakeContainer = { + items: { + create: async (_: any) => { + throw new Error('Create method not implemented'); + }, + }, + } as unknown as Container; + + const mVerify = mock.method(verifyModule, "inputVerified").mock; + mVerify.mockImplementation(() => false); + + const mGetUniqueId = mock.method(dbModule, "getUniqueId").mock; + mGetUniqueId.mockImplementation(() => 'unique-id'); + + const mContainerCreate = mock.method(fakeContainer.items, "create").mock; + + // Arrange: wrong shape of document on purpose. + const doc = { name: 'test' } as unknown as RawInput; + + // Act: + const insertDocumentResult = await insertDocument(fakeContainer, doc); + + // Assert - State verification. + if (isVerificationErrors(insertDocumentResult)) { + assert.deepStrictEqual(insertDocumentResult, { message: 'Verification failed' }); + } else { + throw new Error('Result is not of type VerificationErrors'); + } + + // Assert - Behavior verification: Verify that create was never called. + assert.strictEqual(mContainerCreate.callCount(), 0); + + }); + }); + + it('should return error if db insert fails', async () => { + // Arrange: override inputVerified to return true. + const { input, result } = createTestInputAndResult(); + let errorMessage: string = 'An unknown error occurred'; + + const fakeContainer = { + items: { + create: async (doc: any): Promise => { + return Promise.resolve(null); + }, + }, + } as unknown as Container; + + const mVerify = mock.method(verifyModule, "inputVerified").mock; + mVerify.mockImplementation(() => true); + + const mContainerCreate = mock.method(fakeContainer.items, "create").mock; + mContainerCreate.mockImplementation = async (doc: any) => { + const mockError: DbError = { + message: errorMessage, + code: 500, + }; + throw mockError; + } + + // Act: + const insertDocumentResult = await insertDocument(fakeContainer, input); + + // Assert - Verify type as DbError. + if (isDbError(insertDocumentResult)) { + assert.strictEqual(insertDocumentResult.message, errorMessage); + } else { + throw new Error('Result is not of type DbError'); + } + + // Assert - Ensure create method was called once with the correct arguments. + assert.strictEqual(mContainerCreate.callCount(), 1); + assert.deepStrictEqual(mContainerCreate.calls[0].arguments[0], { + id: input.id, + name: result.name, + }); + }); +}); \ No newline at end of file diff --git a/testrunner/src/mock-function/lib/insert.ts b/testrunner/src/mock-function/lib/insert.ts new file mode 100644 index 00000000..8b9f8871 --- /dev/null +++ b/testrunner/src/mock-function/lib/insert.ts @@ -0,0 +1,41 @@ +// insertDocument.ts +import { Container } from '../data/connect-to-cosmos'; +import { + DbDocument, + DbError, + RawInput, + VerificationErrors, +} from '../data/model'; +import { inputVerified } from '../data/verify'; + +export async function insertDocument( + container: Container, + doc: RawInput, +): Promise { + const isVerified: boolean = inputVerified(doc); + + if (!isVerified) { + return { message: 'Verification failed' } as VerificationErrors; + } + + try { + const { resource } = await container.items.create({ + id: doc.id, + name: `${doc.first} ${doc.last}`, + }); + + return resource as DbDocument; + } catch (error: any) { + if (error instanceof Error) { + if ((error as any).code === 409) { + return { + message: 'Insertion failed: Duplicate entry', + code: 409, + } as DbError; + } + return { message: error.message, code: (error as any).code } as DbError; + } else { + return { message: 'An unknown error occurred', code: 500 } as DbError; + } + } +} diff --git a/testrunner/src/test-boilerplate/boilerplate-with-mock.test-2.ts b/testrunner/src/test-boilerplate/boilerplate-with-mock.test-2.ts new file mode 100644 index 00000000..ded260d2 --- /dev/null +++ b/testrunner/src/test-boilerplate/boilerplate-with-mock.test-2.ts @@ -0,0 +1,38 @@ +import { + describe, + it, + afterEach, + beforeEach, + mock + } from 'node:test' +import assert from 'node:assert'; + +import * as MyService from '../mock-function/data/connect-to-cosmos' + +describe('boilerplate with mock 2', () => { + beforeEach(() =>{ + // Setup required before each test + mock.restoreAll() + }) + afterEach(() => { + // Cleanup required after each test + }); + + it('should if ', async () => { + + const m = mock.method(MyService, "getUniqueId"); + m.mock.mockImplementation(() => { + // Replace the original implementation with a mock result + + // return fake guid + return '12345678-1234-1234-1234-123456789012' + + }); + + const result = await MyService.getUniqueId(); + assert.strictEqual(result, + '12345678-1234-1234-1234-123456789012' + ) + + }) + }) \ No newline at end of file diff --git a/testrunner/src/test-boilerplate/boilerplate-with-mock.test.ts b/testrunner/src/test-boilerplate/boilerplate-with-mock.test.ts new file mode 100644 index 00000000..da8e0f5a --- /dev/null +++ b/testrunner/src/test-boilerplate/boilerplate-with-mock.test.ts @@ -0,0 +1,42 @@ +import { + describe, + it, + afterEach, + beforeEach, + mock + } from 'node:test' + import assert from 'node:assert' + + +// original value is 1 +const result = 1; + +class MyService { + static async myFunction() { + return Promise.resolve(result); + } +} + +describe('boilerplate with mock', () => { + beforeEach(() =>{ + // Setup required before each test + mock.restoreAll() + }) + afterEach(() => { + // Cleanup required after each test + }); + + it('should if ', async () => { + + // Replace the original implementation with a mock, returning 2 + const m = mock.method(MyService, "myFunction").mock; + m.mockImplementation(async () => Promise.resolve(2)) + + + // Test the function, but get the mocked value + const result = await MyService.myFunction(); + assert.strictEqual(result, + 2 + ) + }) + }) \ No newline at end of file diff --git a/testrunner/src/test-boilerplate/boilerplate.test.ts b/testrunner/src/test-boilerplate/boilerplate.test.ts new file mode 100644 index 00000000..ad6e054d --- /dev/null +++ b/testrunner/src/test-boilerplate/boilerplate.test.ts @@ -0,0 +1,28 @@ +import { + describe, + it, + afterEach, + beforeEach, + mock + } from 'node:test' + import assert from 'node:assert' + +describe('boilerplate', () => { + beforeEach(() =>{ + // Setup required before each test + }) + afterEach(() => { + // Cleanup required after each test + }); + + it('should if ', async () => { + // Arrange + // - set up the test data and the expected result + // Act + // - call the function to test + // Assert + // - check the state: result returned from function + // - check the behavior: dependency function calls + + }) + }) \ No newline at end of file diff --git a/testrunner/tsconfig.json b/testrunner/tsconfig.json new file mode 100644 index 00000000..eaff57f9 --- /dev/null +++ b/testrunner/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "es6", // Specify ECMAScript target version + "module": "commonjs", // Specify module code generation + "strict": true, // Enable all strict type-checking options + "esModuleInterop": true, // Enables emit interoperability between CommonJS and ES Modules + "noImplicitAny": false, // Enable error reporting for expressions and declarations with an implied 'any' type + "forceConsistentCasingInFileNames": true, // Disallow inconsistently-cased references to the same file + "outDir": "./dist", + "rootDir": "./", + }, + "include": [ + "src", + "test" + ], + "exclude": [ + "node_modules" + ] + } \ No newline at end of file From b8345fe22b57806845320c738287ff076196e96f Mon Sep 17 00:00:00 2001 From: Dina Berry Date: Thu, 13 Mar 2025 23:32:39 +0000 Subject: [PATCH 14/27] 2 versions --- testrunner-sha/README.md | 29 +++ testrunner-sha/package-lock.json | 30 +++ testrunner-sha/package.json | 17 ++ .../src/app/data/connect-to-cosmos.ts | 39 ++++ testrunner-sha/src/app/data/fake-data.ts | 28 +++ testrunner-sha/src/app/data/model.ts | 39 ++++ testrunner-sha/src/app/data/verify.ts | 9 + testrunner-sha/src/app/index.ts | 17 ++ testrunner-sha/src/app/lib/insert.test.ts | 145 ++++++++++++++ testrunner-sha/src/app/lib/insert.ts | 41 ++++ testrunner-sha/test/basics/01-spies.test.ts | 28 +++ testrunner-sha/test/basics/02-stubs.test.ts | 119 +++++++++++ .../test/in-mem-db/fake-in-mem-db.test.ts | 74 +++++++ .../boilerplate-with-mock.test-2.ts | 38 ++++ .../boilerplate-with-mock.test.ts | 42 ++++ .../test/test-boilerplate/boilerplate.test.ts | 28 +++ testrunner-sha/tsconfig.json | 22 +++ testrunner-typescript/package.json | 4 +- .../src/test-app/insert.test.ts | 117 ++++++----- testrunner/package.json | 2 +- .../src/mock-function/lib/insert.test.ts | 184 +++++++++--------- 21 files changed, 892 insertions(+), 160 deletions(-) create mode 100644 testrunner-sha/README.md create mode 100644 testrunner-sha/package-lock.json create mode 100644 testrunner-sha/package.json create mode 100644 testrunner-sha/src/app/data/connect-to-cosmos.ts create mode 100644 testrunner-sha/src/app/data/fake-data.ts create mode 100644 testrunner-sha/src/app/data/model.ts create mode 100644 testrunner-sha/src/app/data/verify.ts create mode 100644 testrunner-sha/src/app/index.ts create mode 100644 testrunner-sha/src/app/lib/insert.test.ts create mode 100644 testrunner-sha/src/app/lib/insert.ts create mode 100644 testrunner-sha/test/basics/01-spies.test.ts create mode 100644 testrunner-sha/test/basics/02-stubs.test.ts create mode 100644 testrunner-sha/test/in-mem-db/fake-in-mem-db.test.ts create mode 100644 testrunner-sha/test/test-boilerplate/boilerplate-with-mock.test-2.ts create mode 100644 testrunner-sha/test/test-boilerplate/boilerplate-with-mock.test.ts create mode 100644 testrunner-sha/test/test-boilerplate/boilerplate.test.ts create mode 100644 testrunner-sha/tsconfig.json diff --git a/testrunner-sha/README.md b/testrunner-sha/README.md new file mode 100644 index 00000000..376bbee7 --- /dev/null +++ b/testrunner-sha/README.md @@ -0,0 +1,29 @@ +# Unit testing for the Azure SDK for JavaScript + +This subfolder is the source code for the [TBD article](). The purpose is to demonstrate unit test mocks for the Azure SDK for JavaScript. These specific examples use Azure Cosmos DB. + +## To run the test + +1. `npm install` +2. `npm run build` +3. `npm test` + + ```console + > unit-testing@1.0.0 test + > jest dist + + PASS dist/fakes/fake-in-mem-db.spec.js + PASS dist/mock-function/lib/insert.spec.js + + Test Suites: 2 passed, 2 total + Tests: 4 passed, 4 total + Snapshots: 0 total + Time: 4.247 s, estimated 5 s + Ran all test suites matching /dist/i. + ``` + +## Related content + +* [Passwordless connections for Azure services](https://learn.microsoft.com/azure/developer/intro/passwordless-overview) +* [Cosmos DB keyless access to the service](https://learn.microsoft.com/azure/cosmos-db/role-based-access-control) +* [Node.js Test Runner](https://nodejs.org/docs/latest/api/test.html#test-runner) \ No newline at end of file diff --git a/testrunner-sha/package-lock.json b/testrunner-sha/package-lock.json new file mode 100644 index 00000000..613ce82c --- /dev/null +++ b/testrunner-sha/package-lock.json @@ -0,0 +1,30 @@ +{ + "name": "testrunner", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "testrunner", + "version": "1.0.0", + "dependencies": { + "@types/node": "^22.13.10" + } + }, + "node_modules/@types/node": { + "version": "22.13.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.10.tgz", + "integrity": "sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "license": "MIT" + } + } +} diff --git a/testrunner-sha/package.json b/testrunner-sha/package.json new file mode 100644 index 00000000..5ef567f1 --- /dev/null +++ b/testrunner-sha/package.json @@ -0,0 +1,17 @@ +{ + "name": "testrunner-native", + "version": "1.0.0", + "main": "index.js", + "type": "module", + "scripts": { + "build": "rm -rf dist && tsc", + "test": "node --test --experimental-test-coverage --experimental-test-module-mocks", + "test:dev": "npm run build && node --watch --test test/ " + }, + "directories": { + "test": "test" + }, + "dependencies": { + "@types/node": "^22.13.10" + } +} diff --git a/testrunner-sha/src/app/data/connect-to-cosmos.ts b/testrunner-sha/src/app/data/connect-to-cosmos.ts new file mode 100644 index 00000000..bd0b9566 --- /dev/null +++ b/testrunner-sha/src/app/data/connect-to-cosmos.ts @@ -0,0 +1,39 @@ +// connect-to-cosmos.ts + +import { Container, CosmosClient } from '@azure/cosmos'; +import { DefaultAzureCredential } from '@azure/identity'; +import 'dotenv/config'; +import { v4 as uuidv4 } from 'uuid'; + +export { Container }; + +export default class CosmosConnector { + static connectToCosmosWithoutKey(): CosmosClient { + const endpoint = process.env.COSMOS_DB_ENDPOINT!; + const credential = new DefaultAzureCredential(); + const client = new CosmosClient({ endpoint, aadCredentials: credential }); + return client; + } + + static async connectToContainer(): Promise { + const client = CosmosConnector.connectToCosmosWithoutKey(); + const databaseName = process.env.COSMOS_DATABASE_NAME!; + const containerName = process.env.COSMOS_CONTAINER_NAME!; + + // Ensure the database exists + const { database } = await client.databases.createIfNotExists({ + id: databaseName, + }); + + // Ensure the container exists + const { container } = await database.containers.createIfNotExists({ + id: containerName, + }); + + return container; + } + + static getUniqueId(): string { + return uuidv4(); + } +} diff --git a/testrunner-sha/src/app/data/fake-data.ts b/testrunner-sha/src/app/data/fake-data.ts new file mode 100644 index 00000000..6deaa413 --- /dev/null +++ b/testrunner-sha/src/app/data/fake-data.ts @@ -0,0 +1,28 @@ +import { v4 as uuidv4 } from 'uuid'; +import { faker } from '@faker-js/faker'; +import { DbDocument, RawInput } from './model.ts'; + +function createFixture(): T { + const result = { + first: faker.person.firstName(), + last: faker.person.lastName(), + }; + return result as T; +} + +export function createTestInput(): RawInput { + const { first, last } = createFixture(); + return { id: uuidv4(), first, last }; +} + +export function createTestInputAndResult(): { + input: RawInput; + result: Partial; +} { + const input = createTestInput(); + const result = { + id: input.id, + name: `${input.first} ${input.last}`, + }; + return { input, result }; +} diff --git a/testrunner-sha/src/app/data/model.ts b/testrunner-sha/src/app/data/model.ts new file mode 100644 index 00000000..6fd4ba38 --- /dev/null +++ b/testrunner-sha/src/app/data/model.ts @@ -0,0 +1,39 @@ +// input-verified.ts +export interface DbDocument { + id: string; + name: string; +} +export interface DbError { + message: string; + code: number; +} +export interface VerificationErrors { + message: string; +} +export interface RawInput { + id: string; + first: string; + last: string; +} + +export function isDbError(error: any): error is DbError { + return 'message' in error && 'code' in error; +} +export function isVerificationErrors(error: any): error is VerificationErrors { + return 'message' in error; +} +export function validateRawInput(input: any): string[] { + const errors: string[] = []; + + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + if (typeof input.first !== 'string' || input.first.trim().length === 0) { + errors.push('First name is required and must be a non-empty string'); + } + + // eslint-disable-next-line @typescript-eslint/no-unsafe-call + if (typeof input.last !== 'string' || input.last.trim().length === 0) { + errors.push('Last name is required and must be a non-empty string'); + } + + return errors; +} diff --git a/testrunner-sha/src/app/data/verify.ts b/testrunner-sha/src/app/data/verify.ts new file mode 100644 index 00000000..1d507135 --- /dev/null +++ b/testrunner-sha/src/app/data/verify.ts @@ -0,0 +1,9 @@ +// input-verified.ts +import { validateRawInput } from './model.ts'; + +export default class Verfiy { + static inputVerified(doc: any): boolean { + const result = validateRawInput(doc); + return result.length === 0; + } +} diff --git a/testrunner-sha/src/app/index.ts b/testrunner-sha/src/app/index.ts new file mode 100644 index 00000000..b75a8a00 --- /dev/null +++ b/testrunner-sha/src/app/index.ts @@ -0,0 +1,17 @@ +import CosmosConnector from './data/connect-to-cosmos.ts'; +import { createTestInput } from './data/fake-data.ts'; +import type { DbDocument, DbError, VerificationErrors } from './data/model.ts'; +import { insertDocument } from './lib/insert.ts'; + +async function main(): Promise { + const container = await CosmosConnector.connectToContainer(); + const input = createTestInput(); + return await insertDocument(container, input); +} + +main() + .then((doc) => console.log(doc)) + .catch((error) => { + console.error(error); + process.exit(1); + }); diff --git a/testrunner-sha/src/app/lib/insert.test.ts b/testrunner-sha/src/app/lib/insert.test.ts new file mode 100644 index 00000000..cc28563a --- /dev/null +++ b/testrunner-sha/src/app/lib/insert.test.ts @@ -0,0 +1,145 @@ +// insertDocument.test.ts +import assert from 'node:assert'; +import { beforeEach, describe, it, mock } from 'node:test'; +import { Container } from '../data/connect-to-cosmos.ts'; +import { createTestInputAndResult } from '../data/fake-data.ts'; +import type { + DbDocument, + DbError, + RawInput +} from '../data/model.ts'; +import { + isDbError, + isVerificationErrors +} from '../data/model.ts'; +// Instead of jest.mock, import the whole module to override functions as needed. +import CosmosConnector from '../data/connect-to-cosmos.ts'; +import Verify from '../data/verify.ts'; +import { insertDocument } from './insert.ts'; + +// --- Test suite for insertDocument --- +describe('Insert into db', () => { + + // Set up a fresh container object before each test. + beforeEach(() => { + // Setup required before each test + mock.restoreAll() + }) + + it("Success", () => { + it('should insert document successfully', async () => { + // Arrange: override inputVerified to return true. + const { input, result }: { input: RawInput; result: Partial } = createTestInputAndResult(); + + const fakeContainer = { + items: { + create: async (doc: any) => { + return { resource: result }; + }, + }, + } as unknown as Container; + + const mVerify = mock.method(Verify, "inputVerified").mock; + mVerify.mockImplementation(() => true); + + const mContainerCreate = mock.method(fakeContainer.items as any, "create").mock; + mContainerCreate.mockImplementation(async (doc: any) => { + return { resource: result }; + }); + + // Act: + const receivedResult = await insertDocument(fakeContainer, input); + + // Assert - State verification: Ensure the result is as expected. + assert.deepStrictEqual(receivedResult, result); + + // Assert - Behavior verification: Ensure create was called once with correct arguments. + assert.strictEqual(mContainerCreate.callCount(), 1); + assert.deepStrictEqual(mContainerCreate.calls[0].arguments[0], { + id: input.id, + name: result.name, + }); + }); + }); + + it("Failure", () => { + it('should return verification error if input is not verified', async () => { + + const fakeContainer = { + items: { + create: async (_: any) => { + throw new Error('Create method not implemented'); + }, + }, + } as unknown as Container; + + const mVerify = mock.method(Verify, "inputVerified").mock; + mVerify.mockImplementation(() => false); + + const mGetUniqueId = mock.method(CosmosConnector, "getUniqueId").mock; + mGetUniqueId.mockImplementation(() => 'unique-id'); + + const mContainerCreate = mock.method(fakeContainer.items, "create").mock; + + // Arrange: wrong shape of document on purpose. + const doc = { name: 'test' } as unknown as RawInput; + + // Act: + const insertDocumentResult = await insertDocument(fakeContainer, doc); + + // Assert - State verification. + if (isVerificationErrors(insertDocumentResult)) { + assert.deepStrictEqual(insertDocumentResult, { message: 'Verification failed' }); + } else { + throw new Error('Result is not of type VerificationErrors'); + } + + // Assert - Behavior verification: Verify that create was never called. + assert.strictEqual(mContainerCreate.callCount(), 0); + + }); + }); + + it('should return error if db insert fails', async () => { + // Arrange: override inputVerified to return true. + const { input, result } = createTestInputAndResult(); + let errorMessage: string = 'An unknown error occurred'; + + const fakeContainer = { + items: { + create: async (doc: any): Promise => { + return Promise.resolve(null); + }, + }, + } as unknown as Container; + + const mVerify = mock.method(Verify, "inputVerified").mock; + mVerify.mockImplementation(() => true); + + const mContainerCreate = mock.method(fakeContainer.items, "create").mock; + mContainerCreate.mockImplementation = async (doc: any) => { + const mockError: DbError = { + message: errorMessage, + code: 500, + }; + throw mockError; + } + + // Act: + const insertDocumentResult = await insertDocument(fakeContainer, input); + + // Assert - Verify type as DbError. + if (isDbError(insertDocumentResult)) { + assert.strictEqual(insertDocumentResult.message, errorMessage); + } else { + throw new Error('Result is not of type DbError'); + } + + // Assert - Ensure create method was called once with the correct arguments. + assert.strictEqual(mContainerCreate.callCount(), 1); + assert.deepStrictEqual(mContainerCreate.calls[0].arguments[0], { + id: input.id, + name: result.name, + }); + }); +}); \ No newline at end of file diff --git a/testrunner-sha/src/app/lib/insert.ts b/testrunner-sha/src/app/lib/insert.ts new file mode 100644 index 00000000..4549bc07 --- /dev/null +++ b/testrunner-sha/src/app/lib/insert.ts @@ -0,0 +1,41 @@ +// insertDocument.ts +import { Container } from '../data/connect-to-cosmos.ts'; +import type { + DbDocument, + DbError, + RawInput, + VerificationErrors, +} from '../data/model.ts'; +import Verify from '../data/verify.ts'; + +export async function insertDocument( + container: Container, + doc: RawInput, +): Promise { + const isVerified: boolean = Verify.inputVerified(doc); + + if (!isVerified) { + return { message: 'Verification failed' } as VerificationErrors; + } + + try { + const { resource } = await container.items.create({ + id: doc.id, + name: `${doc.first} ${doc.last}`, + }); + + return resource as DbDocument; + } catch (error: any) { + if (error instanceof Error) { + if ((error as any).code === 409) { + return { + message: 'Insertion failed: Duplicate entry', + code: 409, + } as DbError; + } + return { message: error.message, code: (error as any).code } as DbError; + } else { + return { message: 'An unknown error occurred', code: 500 } as DbError; + } + } +} diff --git a/testrunner-sha/test/basics/01-spies.test.ts b/testrunner-sha/test/basics/01-spies.test.ts new file mode 100644 index 00000000..8bcbea13 --- /dev/null +++ b/testrunner-sha/test/basics/01-spies.test.ts @@ -0,0 +1,28 @@ +import { + describe, + it, + mock +} from 'node:test'; +import assert from 'node:assert'; + + +function run({fn, times}){ + for(let i = 0; i < times; i++){ + fn({current: i * 5}); + } +} + +describe('Spies', () => { + it('should verify calls in a mock', () => { + const spy = mock.fn(); + run({fn: spy, times: 3}); + + assert.strictEqual(spy.mock.callCount(), 3); + const calls = spy.mock.calls; + + assert.deepStrictEqual(calls[0].arguments[0], {current: 0}); + assert.deepStrictEqual(calls[1].arguments[0], {current: 5}); + assert.deepStrictEqual(calls[2].arguments[0], {current: 10}); + + }); +}); \ No newline at end of file diff --git a/testrunner-sha/test/basics/02-stubs.test.ts b/testrunner-sha/test/basics/02-stubs.test.ts new file mode 100644 index 00000000..207abfe4 --- /dev/null +++ b/testrunner-sha/test/basics/02-stubs.test.ts @@ -0,0 +1,119 @@ +import { + describe, + it, + beforeEach, + mock +} from 'node:test' +import assert from 'node:assert' + +class Service { + static async getTalks({ skip, limit }) { + const items = await fetch('https://tml-api.herokuapp.com/graphql', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + query: ` + { + getTalks (skip: ${skip}, limit: ${limit}) { + totalCount, + talks { + _id + title + } + } + } + ` + }) + }) + return (await items.json()).data.getTalks.talks + } +} + +function mapResponse(data) { + return data + .map(({ _id, title }, index) => `[${index}] id: ${_id}, title: ${title}`) + .join('\n') +} + +async function run({ skip = 0, limit = 10 }) { + const talks = mapResponse(await Service.getTalks({ skip, limit })) + return talks +} + +describe('Stub Test Suite', () => { + beforeEach(() => mock.restoreAll()) + + it('should stub APIs', async () => { + const m = mock.method( + Service, + "getTalks", + ).mock; + m.mockImplementation(async () => [ + { + _id: '63865750c839dbaacd8116e1', + title: 'The Journey About How I Fixed a Bug in the Node.js Core That Affected Thousands of Packages' + } + ]) + + const result = await run({ limit: 1 }) + const expected = `[0] id: 63865750c839dbaacd8116e1, title: The Journey About How I Fixed a Bug in the Node.js Core That Affected Thousands of Packages` + + assert.deepStrictEqual(m.callCount(), 1) + const calls = m.calls + + assert.deepStrictEqual(calls[0].arguments[0], { skip: 0, limit: 1 }) + assert.strictEqual(result, expected) + }) + + it('should stub different values for API calls', async () => { + const m = mock.method( + Service, + "getTalks", + ).mock + + m.mockImplementationOnce(async () => [ + { + _id: '63865750c839dbaacd8116e1', + title: 'The Journey About How I Fixed a Bug in the Node.js Core That Affected Thousands of Packages' + } + ], 0) + + m.mockImplementationOnce(async () => [ + { + _id: '01', + title: 'Mock 01' + } + ], 1) + + m.mockImplementationOnce(async () => [ + { + _id: '02', + title: 'Mock 02' + } + ], 2) + + { + const result = await run({ skip: 0, limit: 1 }) + const expected = `[0] id: 63865750c839dbaacd8116e1, title: The Journey About How I Fixed a Bug in the Node.js Core That Affected Thousands of Packages` + assert.strictEqual(result, expected) + } + { + const result = await run({ skip: 1, limit: 1 }) + const expected = `[0] id: 01, title: Mock 01` + assert.strictEqual(result, expected) + } + { + const result = await run({ skip: 2, limit: 1 }) + const expected = `[0] id: 02, title: Mock 02` + assert.strictEqual(result, expected) + } + + const calls = m.calls + assert.strictEqual(m.callCount(), 3) + assert.deepStrictEqual(calls[0].arguments[0], { skip: 0, limit: 1 }) + assert.deepStrictEqual(calls[1].arguments[0], { skip: 1, limit: 1 }) + assert.deepStrictEqual(calls[2].arguments[0], { skip: 2, limit: 1 }) + }) +}) \ No newline at end of file diff --git a/testrunner-sha/test/in-mem-db/fake-in-mem-db.test.ts b/testrunner-sha/test/in-mem-db/fake-in-mem-db.test.ts new file mode 100644 index 00000000..103000d6 --- /dev/null +++ b/testrunner-sha/test/in-mem-db/fake-in-mem-db.test.ts @@ -0,0 +1,74 @@ +// fake-in-mem-db.spec.ts +import { describe, it, beforeEach, afterEach } from 'node:test'; +import assert from 'node:assert'; + +class FakeDatabase { + private data: Record; + + constructor() { + this.data = {}; + } + + save(key: string, value: any): void { + this.data[key] = value; + } + + get(key: string): any { + return this.data[key]; + } +} + +// Function to test +function someTestFunction(db: FakeDatabase, key: string, value: any): any { + db.save(key, value); + return db.get(key); +} + +describe('In-Mem DB', () => { + let fakeDb: FakeDatabase; + let testKey: string; + let testValue: any; + let originalSave: (key: string, value: any) => void; + let saveSpyCallCount: number; + let saveSpyArgs: Array<[string, any]>; + + beforeEach(() => { + fakeDb = new FakeDatabase(); + testKey = 'testKey'; + testValue = { + first: 'John', + last: 'Jones', + lastUpdated: new Date().toISOString(), + }; + + // Create a simple spy on the save method + originalSave = fakeDb.save.bind(fakeDb); + saveSpyCallCount = 0; + saveSpyArgs = []; + fakeDb.save = function (key: string, value: any): void { + saveSpyCallCount++; + saveSpyArgs.push([key, value]); + return originalSave(key, value); + }; + }); + + afterEach(() => { + // Restore original method if necessary + fakeDb.save = originalSave; + }); + + it('should save and return the correct value', () => { + // Call the function under test + const result = someTestFunction(fakeDb, testKey, testValue); + + // Verify state + assert.deepStrictEqual(result, testValue); + assert.strictEqual(result.first, 'John'); + assert.strictEqual(result.last, 'Jones'); + assert.strictEqual(result.lastUpdated, testValue.lastUpdated); + + // Verify behavior using our manual spy + assert.strictEqual(saveSpyCallCount, 1); + assert.deepStrictEqual(saveSpyArgs[0], [testKey, testValue]); + }); +}); \ No newline at end of file diff --git a/testrunner-sha/test/test-boilerplate/boilerplate-with-mock.test-2.ts b/testrunner-sha/test/test-boilerplate/boilerplate-with-mock.test-2.ts new file mode 100644 index 00000000..3d7614fb --- /dev/null +++ b/testrunner-sha/test/test-boilerplate/boilerplate-with-mock.test-2.ts @@ -0,0 +1,38 @@ +import { + describe, + it, + afterEach, + beforeEach, + mock + } from 'node:test' +import assert from 'node:assert'; + +import CosmosConnector from '../../src/app/data/connect-to-cosmos.ts'; + +describe('boilerplate with mock 2', () => { + beforeEach(() =>{ + // Setup required before each test + mock.restoreAll() + }) + afterEach(() => { + // Cleanup required after each test + }); + + it('should if ', async () => { + + const m = mock.method(CosmosConnector, "getUniqueId"); + m.mock.mockImplementation(() => { + // Replace the original implementation with a mock result + + // return fake guid + return '12345678-1234-1234-1234-123456789012' + + }); + + const result = await CosmosConnector.getUniqueId(); + assert.strictEqual(result, + '12345678-1234-1234-1234-123456789012' + ) + + }) + }) \ No newline at end of file diff --git a/testrunner-sha/test/test-boilerplate/boilerplate-with-mock.test.ts b/testrunner-sha/test/test-boilerplate/boilerplate-with-mock.test.ts new file mode 100644 index 00000000..da8e0f5a --- /dev/null +++ b/testrunner-sha/test/test-boilerplate/boilerplate-with-mock.test.ts @@ -0,0 +1,42 @@ +import { + describe, + it, + afterEach, + beforeEach, + mock + } from 'node:test' + import assert from 'node:assert' + + +// original value is 1 +const result = 1; + +class MyService { + static async myFunction() { + return Promise.resolve(result); + } +} + +describe('boilerplate with mock', () => { + beforeEach(() =>{ + // Setup required before each test + mock.restoreAll() + }) + afterEach(() => { + // Cleanup required after each test + }); + + it('should if ', async () => { + + // Replace the original implementation with a mock, returning 2 + const m = mock.method(MyService, "myFunction").mock; + m.mockImplementation(async () => Promise.resolve(2)) + + + // Test the function, but get the mocked value + const result = await MyService.myFunction(); + assert.strictEqual(result, + 2 + ) + }) + }) \ No newline at end of file diff --git a/testrunner-sha/test/test-boilerplate/boilerplate.test.ts b/testrunner-sha/test/test-boilerplate/boilerplate.test.ts new file mode 100644 index 00000000..ad6e054d --- /dev/null +++ b/testrunner-sha/test/test-boilerplate/boilerplate.test.ts @@ -0,0 +1,28 @@ +import { + describe, + it, + afterEach, + beforeEach, + mock + } from 'node:test' + import assert from 'node:assert' + +describe('boilerplate', () => { + beforeEach(() =>{ + // Setup required before each test + }) + afterEach(() => { + // Cleanup required after each test + }); + + it('should if ', async () => { + // Arrange + // - set up the test data and the expected result + // Act + // - call the function to test + // Assert + // - check the state: result returned from function + // - check the behavior: dependency function calls + + }) + }) \ No newline at end of file diff --git a/testrunner-sha/tsconfig.json b/testrunner-sha/tsconfig.json new file mode 100644 index 00000000..af542786 --- /dev/null +++ b/testrunner-sha/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "target": "es6", // Specify ECMAScript target version + "module": "commonjs", // Specify module code generation + "strict": true, // Enable all strict type-checking options + "esModuleInterop": true, // Enables emit interoperability between CommonJS and ES Modules + "noImplicitAny": false, // Enable error reporting for expressions and declarations with an implied 'any' type + "forceConsistentCasingInFileNames": true, // Disallow inconsistently-cased references to the same file + "outDir": "./dist", + "rootDir": "./", + "erasableSyntaxOnly": true, + "allowImportingTsExtensions": true, + "noEmit": true + }, + "include": [ + "src", + "test" + ], + "exclude": [ + "node_modules" + ] + } \ No newline at end of file diff --git a/testrunner-typescript/package.json b/testrunner-typescript/package.json index 69af8966..5b9449f2 100644 --- a/testrunner-typescript/package.json +++ b/testrunner-typescript/package.json @@ -1,10 +1,10 @@ { - "name": "testrunner", + "name": "testrunner-typescript", "version": "1.0.0", "main": "index.js", "scripts": { "build": "rm -rf dist && tsc", - "test": "npm run build && node --test --no-experimental-strip-types --experimental-test-coverage --experimental-test-module-mocks", + "test": "npm run build && node --test --experimental-test-coverage --experimental-test-module-mocks --trace-exit", "test:dev": "npm run build && node --watch --test test/ " }, "directories": { diff --git a/testrunner-typescript/src/test-app/insert.test.ts b/testrunner-typescript/src/test-app/insert.test.ts index 0b4a0684..ce3615d7 100644 --- a/testrunner-typescript/src/test-app/insert.test.ts +++ b/testrunner-typescript/src/test-app/insert.test.ts @@ -26,80 +26,78 @@ describe('Insert into db', () => { mock.restoreAll() }) - it("Success", () => { - it('should insert document successfully', async () => { - // Arrange: override inputVerified to return true. - const { input, result }: { input: RawInput; result: Partial } = createTestInputAndResult(); - - const fakeContainer = { - items: { - create: async (doc: any) => { - return { resource: result }; - }, + it('should insert document successfully', async () => { + // Arrange: override inputVerified to return true. + const { input, result }: { input: RawInput; result: Partial } = createTestInputAndResult(); + + const fakeContainer = { + items: { + create: async (doc: any) => { + return { resource: result }; }, - } as unknown as Container; + }, + } as unknown as Container; - const mVerify = mock.method(Verify, "inputVerified").mock; - mVerify.mockImplementation(() => true); + const mVerify = mock.method(Verify, "inputVerified").mock; + mVerify.mockImplementation(() => true); - const mContainerCreate = mock.method(fakeContainer.items as any, "create").mock; - mContainerCreate.mockImplementation(async (doc: any) => { - return { resource: result }; - }); + const mContainerCreate = mock.method(fakeContainer.items as any, "create").mock; + mContainerCreate.mockImplementation(async (doc: any) => { + return { resource: result }; + }); - // Act: - const receivedResult = await insertDocument(fakeContainer, input); + // Act: + const receivedResult = await insertDocument(fakeContainer, input); - // Assert - State verification: Ensure the result is as expected. - assert.deepStrictEqual(receivedResult, result); + // Assert - State verification: Ensure the result is as expected. + assert.deepStrictEqual(receivedResult, result); - // Assert - Behavior verification: Ensure create was called once with correct arguments. - assert.strictEqual(mContainerCreate.callCount(), 1); - assert.deepStrictEqual(mContainerCreate.calls[0].arguments[0], { - id: input.id, - name: result.name, - }); + // Assert - Behavior verification: Ensure create was called once with correct arguments. + assert.strictEqual(mContainerCreate.callCount(), 1); + assert.deepStrictEqual(mContainerCreate.calls[0].arguments[0], { + id: input.id, + name: result.name, }); }); - it("Failure", () => { - it('should return verification error if input is not verified', async () => { - const fakeContainer = { - items: { - create: async (_: any) => { - throw new Error('Create method not implemented'); - }, + it('should return verification error if input is not verified', async () => { + + const fakeContainer = { + items: { + create: async (_: any) => { + throw new Error('Create method not implemented'); }, - } as unknown as Container; + }, + } as unknown as Container; - const mVerify = mock.method(Verify, "inputVerified").mock; - mVerify.mockImplementation(() => false); + const mVerify = mock.method(Verify, "inputVerified").mock; + mVerify.mockImplementation(() => false); - const mGetUniqueId = mock.method(CosmosConnector, "getUniqueId").mock; - mGetUniqueId.mockImplementation(() => 'unique-id'); + const mGetUniqueId = mock.method(CosmosConnector, "getUniqueId").mock; + mGetUniqueId.mockImplementation(() => 'unique-id'); - const mContainerCreate = mock.method(fakeContainer.items, "create").mock; + const mContainerCreate = mock.method(fakeContainer.items, "create").mock; - // Arrange: wrong shape of document on purpose. - const doc = { name: 'test' } as unknown as RawInput; + // Arrange: wrong shape of document on purpose. + const doc = { name: 'test' } as unknown as RawInput; - // Act: - const insertDocumentResult = await insertDocument(fakeContainer, doc); + // Act: + const insertDocumentResult = await insertDocument(fakeContainer, doc); - // Assert - State verification. - if (isVerificationErrors(insertDocumentResult)) { - assert.deepStrictEqual(insertDocumentResult, { message: 'Verification failed' }); - } else { - throw new Error('Result is not of type VerificationErrors'); - } + // Assert - State verification. + if (isVerificationErrors(insertDocumentResult)) { + assert.deepStrictEqual(insertDocumentResult, { message: 'Verification failed' }); + } else { + throw new Error('Result is not of type VerificationErrors'); + } - // Assert - Behavior verification: Verify that create was never called. - assert.strictEqual(mContainerCreate.callCount(), 0); + // Assert - Behavior verification: Verify that create was never called. + assert.strictEqual(mContainerCreate.callCount(), 0); - }); }); + it('should return error if db insert fails', async () => { // Arrange: override inputVerified to return true. const { input, result } = createTestInputAndResult(); @@ -128,18 +126,13 @@ describe('Insert into db', () => { // Act: const insertDocumentResult = await insertDocument(fakeContainer, input); - // Assert - Verify type as DbError. - if (isDbError(insertDocumentResult)) { - assert.strictEqual(insertDocumentResult.message, errorMessage); - } else { - throw new Error('Result is not of type DbError'); - } - - // Assert - Ensure create method was called once with the correct arguments. + // // Assert - Ensure create method was called once with the correct arguments. + assert.strictEqual(isDbError(insertDocumentResult), true); assert.strictEqual(mContainerCreate.callCount(), 1); + assert.deepStrictEqual(mContainerCreate.calls[0].arguments[0], { id: input.id, name: result.name, }); }); -}); \ No newline at end of file +}); diff --git a/testrunner/package.json b/testrunner/package.json index ea96876c..b453d91b 100644 --- a/testrunner/package.json +++ b/testrunner/package.json @@ -4,7 +4,7 @@ "main": "index.js", "scripts": { "build": "rm -rf dist && tsc", - "test": "npm run build && NODE_DEBUG_NATIVE=node:test NODE_DEBUG=node:test node --test --experimental-test-coverage --experimental-test-module-mocks", + "test": "npm run build && node --test --experimental-test-coverage --experimental-test-module-mocks --trace-exit", "test:dev": "npm run build && node --watch --test test/ " }, "directories": { diff --git a/testrunner/src/mock-function/lib/insert.test.ts b/testrunner/src/mock-function/lib/insert.test.ts index 31c884ef..69435df7 100644 --- a/testrunner/src/mock-function/lib/insert.test.ts +++ b/testrunner/src/mock-function/lib/insert.test.ts @@ -3,12 +3,14 @@ import { describe, it, beforeEach, mock } from 'node:test'; import assert from 'node:assert'; import { Container } from '../data/connect-to-cosmos'; import { createTestInputAndResult } from '../data/fake-data'; -import { +import type { DbDocument, DbError, + RawInput, +} from '../data/model'; +import { isDbError, isVerificationErrors, - RawInput, } from '../data/model'; // Instead of jest.mock, import the whole module to override functions as needed. import * as verifyModule from '../data/verify'; @@ -24,120 +26,112 @@ describe('Insert into db', () => { mock.restoreAll() }) - test("Success", () => { - it('should insert document successfully', async () => { - // Arrange: override inputVerified to return true. - const { input, result }: { input: RawInput; result: Partial } = createTestInputAndResult(); + it('should insert document successfully', async () => { + // Arrange: override inputVerified to return true. + const { input, result }: { input: RawInput; result: Partial } = createTestInputAndResult(); - const fakeContainer = { - items: { - create: async (doc: any) => { - return { resource: result }; - }, + const fakeContainer = { + items: { + create: async (doc: any) => { + return { resource: result }; }, - } as unknown as Container; + }, + } as unknown as Container; - const mVerify = mock.method(verifyModule, "inputVerified").mock; - mVerify.mockImplementation(() => true); + const mVerify = mock.method(verifyModule, "inputVerified").mock; + mVerify.mockImplementation(() => true); - const mContainerCreate = mock.method(fakeContainer.items as any, "create").mock; - mContainerCreate.mockImplementation(async (doc: any) => { - return { resource: result }; - }); + const mContainerCreate = mock.method(fakeContainer.items as any, "create").mock; + mContainerCreate.mockImplementation(async (doc: any) => { + return { resource: result }; + }); - // Act: - const receivedResult = await insertDocument(fakeContainer, input); + // Act: + const receivedResult = await insertDocument(fakeContainer, input); - // Assert - State verification: Ensure the result is as expected. - assert.deepStrictEqual(receivedResult, result); + // Assert - State verification: Ensure the result is as expected. + assert.deepStrictEqual(receivedResult, result); - // Assert - Behavior verification: Ensure create was called once with correct arguments. - assert.strictEqual(mContainerCreate.callCount(), 1); - assert.deepStrictEqual(mContainerCreate.calls[0].arguments[0], { - id: input.id, - name: result.name, - }); + // Assert - Behavior verification: Ensure create was called once with correct arguments. + assert.strictEqual(mContainerCreate.callCount(), 1); + assert.deepStrictEqual(mContainerCreate.calls[0].arguments[0], { + id: input.id, + name: result.name, }); }); +}); - test("Failure", () => { - it('should return verification error if input is not verified', async () => { - - const fakeContainer = { - items: { - create: async (_: any) => { - throw new Error('Create method not implemented'); - }, - }, - } as unknown as Container; - - const mVerify = mock.method(verifyModule, "inputVerified").mock; - mVerify.mockImplementation(() => false); - - const mGetUniqueId = mock.method(dbModule, "getUniqueId").mock; - mGetUniqueId.mockImplementation(() => 'unique-id'); - const mContainerCreate = mock.method(fakeContainer.items, "create").mock; +it('should return verification error if input is not verified', async () => { - // Arrange: wrong shape of document on purpose. - const doc = { name: 'test' } as unknown as RawInput; + const fakeContainer = { + items: { + create: async (_: any) => { + throw new Error('Create method not implemented'); + }, + }, + } as unknown as Container; - // Act: - const insertDocumentResult = await insertDocument(fakeContainer, doc); + const mVerify = mock.method(verifyModule, "inputVerified").mock; + mVerify.mockImplementation(() => false); - // Assert - State verification. - if (isVerificationErrors(insertDocumentResult)) { - assert.deepStrictEqual(insertDocumentResult, { message: 'Verification failed' }); - } else { - throw new Error('Result is not of type VerificationErrors'); - } + const mGetUniqueId = mock.method(dbModule, "getUniqueId").mock; + mGetUniqueId.mockImplementation(() => 'unique-id'); - // Assert - Behavior verification: Verify that create was never called. - assert.strictEqual(mContainerCreate.callCount(), 0); + const mContainerCreate = mock.method(fakeContainer.items, "create").mock; - }); - }); + // Arrange: wrong shape of document on purpose. + const doc = { name: 'test' } as unknown as RawInput; - it('should return error if db insert fails', async () => { - // Arrange: override inputVerified to return true. - const { input, result } = createTestInputAndResult(); - let errorMessage: string = 'An unknown error occurred'; + // Act: + const insertDocumentResult = await insertDocument(fakeContainer, doc); - const fakeContainer = { - items: { - create: async (doc: any): Promise => { - return Promise.resolve(null); - }, - }, - } as unknown as Container; + // Assert - State verification. + if (isVerificationErrors(insertDocumentResult)) { + assert.deepStrictEqual(insertDocumentResult, { message: 'Verification failed' }); + } else { + throw new Error('Result is not of type VerificationErrors'); + } - const mVerify = mock.method(verifyModule, "inputVerified").mock; - mVerify.mockImplementation(() => true); + // Assert - Behavior verification: Verify that create was never called. + assert.strictEqual(mContainerCreate.callCount(), 0); - const mContainerCreate = mock.method(fakeContainer.items, "create").mock; - mContainerCreate.mockImplementation = async (doc: any) => { - const mockError: DbError = { - message: errorMessage, - code: 500, - }; - throw mockError; - } +}); - // Act: - const insertDocumentResult = await insertDocument(fakeContainer, input); - // Assert - Verify type as DbError. - if (isDbError(insertDocumentResult)) { - assert.strictEqual(insertDocumentResult.message, errorMessage); - } else { - throw new Error('Result is not of type DbError'); - } +it('should return error if db insert fails', async () => { + // Arrange: override inputVerified to return true. + const { input, result } = createTestInputAndResult(); + let errorMessage: string = 'An unknown error occurred'; - // Assert - Ensure create method was called once with the correct arguments. - assert.strictEqual(mContainerCreate.callCount(), 1); - assert.deepStrictEqual(mContainerCreate.calls[0].arguments[0], { - id: input.id, - name: result.name, - }); + const fakeContainer = { + items: { + create: async (doc: any): Promise => { + return Promise.resolve(null); + }, + }, + } as unknown as Container; + + const mVerify = mock.method(verifyModule, "inputVerified").mock; + mVerify.mockImplementation(() => true); + + const mContainerCreate = mock.method(fakeContainer.items, "create").mock; + mContainerCreate.mockImplementation = async (doc: any) => { + const mockError: DbError = { + message: errorMessage, + code: 500, + }; + throw mockError; + } + + // Act: + const insertDocumentResult = await insertDocument(fakeContainer, input); + + // // Assert - Ensure create method was called once with the correct arguments. + assert.strictEqual(isDbError(insertDocumentResult), true); + assert.strictEqual(mContainerCreate.callCount(), 1); + assert.deepStrictEqual(mContainerCreate.calls[0].arguments[0], { + id: input.id, + name: result.name, }); -}); \ No newline at end of file +}); From a4dca92ca7e37895fbb65c1567f2bad7d7edb4f6 Mon Sep 17 00:00:00 2001 From: Dina Berry Date: Fri, 14 Mar 2025 16:52:59 +0000 Subject: [PATCH 15/27] wop test runner build and test wip ignore coverage and update readmes --- .gitignore | 3 + package-lock.json | 2050 +++++++++++++++-- package.json | 10 +- test-with-jest/.editorconfig | 10 + test-with-jest/.eslintrc.json | 29 + test-with-jest/.gitignore | 3 + test-with-jest/README.md | 41 + test-with-jest/package.json | 43 + test-with-jest/sample.env | 9 + test-with-jest/scripts/create-resources.sh | 55 + .../src/fakes/fake-in-mem-db.spec.ts | 61 + .../mock-function/data/connect-to-cosmos.ts | 0 .../src/mock-function}/data/fake-data.ts | 0 .../src/mock-function/data/model.ts | 0 .../src/mock-function/data/verify.ts | 0 .../src/mock-function/index.ts | 0 .../src/mock-function/lib/insert.spec.ts | 123 + .../src/mock-function/lib/insert.ts | 0 .../boilerplate-with-mock.spec.ts | 25 + .../src/test-boilerplate/boilerplate.spec.ts | 20 + {testrunner => test-with-jest}/tsconfig.json | 13 +- test-with-node-testrunner/README.md | 73 + test-with-node-testrunner/package.json | 19 + .../src}/data/connect-to-cosmos.ts | 0 .../src}/data/fake-data.ts | 2 +- .../src}/data/model.ts | 0 .../src}/data/verify.ts | 2 +- .../src}/index.ts | 8 +- .../src}/lib/insert.ts | 6 +- .../test}/01-spies.test.ts | 0 .../test/02-stubs.test.ts | 85 + .../test}/boilerplate-with-mock.test.ts | 0 .../test}/boilerplate.test.ts | 0 .../test}/fake-in-mem-db.test.ts | 34 +- .../test}/insert.test.ts | 31 +- test-with-node-testrunner/tsconfig.json | 19 + test-with-vitest/README.md | 52 + .../coverage/lcov-report/base.css | 224 ++ .../coverage/lcov-report/block-navigation.js | 87 + .../coverage/lcov-report/favicon.png | Bin 0 -> 445 bytes .../coverage/lcov-report/index.html | 146 ++ .../coverage/lcov-report/prettify.css | 1 + .../coverage/lcov-report/prettify.js | 2 + .../lcov-report/sort-arrow-sprite.png | Bin 0 -> 138 bytes .../coverage/lcov-report/sorter.js | 196 ++ .../src/data/connect-to-cosmos.ts.html | 202 ++ .../lcov-report/src/data/fake-data.ts.html | 169 ++ .../coverage/lcov-report/src/data/index.html | 161 ++ .../lcov-report/src/data/model.ts.html | 202 ++ .../lcov-report/src/data/verify.ts.html | 112 + .../coverage/lcov-report/src/index.html | 116 + .../coverage/lcov-report/src/index.ts.html | 136 ++ .../coverage/lcov-report/src/lib/index.html | 116 + .../lcov-report/src/lib/insert.ts.html | 208 ++ test-with-vitest/coverage/lcov.info | 144 ++ test-with-vitest/package.json | 26 + .../src}/data/connect-to-cosmos.ts | 0 .../src}/data/fake-data.ts | 2 +- .../src}/data/model.ts | 0 .../src}/data/verify.ts | 2 +- .../src/app => test-with-vitest/src}/index.ts | 8 +- .../src}/lib/insert.ts | 6 +- test-with-vitest/tests/01-spies.test.ts | 18 + test-with-vitest/tests/02-stubs.test.ts | 88 + .../tests/boilerplate-with-mock.test.ts | 29 + test-with-vitest/tests/boilerplate.test.ts | 26 + test-with-vitest/tests/fake-in-mem-db.test.ts | 63 + test-with-vitest/tests/insert.test.ts | 127 + test-with-vitest/tsconfig.json | 17 + test-with-vitest/vitest.config.ts | 17 + testrunner-sha/README.md | 29 - testrunner-sha/package-lock.json | 30 - testrunner-sha/package.json | 17 - testrunner-sha/src/app/lib/insert.test.ts | 145 -- testrunner-sha/test/basics/02-stubs.test.ts | 119 - .../boilerplate-with-mock.test-2.ts | 38 - testrunner-sha/tsconfig.json | 22 - testrunner-typescript/README.md | 29 - testrunner-typescript/package-lock.json | 30 - testrunner-typescript/package.json | 16 - .../test-app/boilerplate-with-mock.test-2.ts | 38 - .../src/test-basics/01-spies.test.ts | 28 - .../src/test-basics/02-stubs.test.ts | 119 - .../boilerplate-with-mock.test.ts | 42 - .../src/test-boilerplate/boilerplate.test.ts | 28 - .../src/test-fakes/fake-in-mem-db.test.ts | 74 - testrunner-typescript/tsconfig.json | 20 - testrunner/README.md | 29 - testrunner/package-lock.json | 30 - testrunner/package.json | 16 - testrunner/src/basics/01-spies.test.ts | 28 - testrunner/src/basics/02-stubs.test.ts | 119 - testrunner/src/fakes/fake-in-mem-db.test.ts | 74 - .../src/mock-function/lib/insert.test.ts | 137 -- .../boilerplate-with-mock.test-2.ts | 38 - .../boilerplate-with-mock.test.ts | 42 - .../src/test-boilerplate/boilerplate.test.ts | 28 - unit-testing/package.json | 4 +- .../src/mock-function/lib/insert.spec.ts | 2 +- unit-testing/tsconfig.json | 4 +- 100 files changed, 5245 insertions(+), 1607 deletions(-) create mode 100644 test-with-jest/.editorconfig create mode 100644 test-with-jest/.eslintrc.json create mode 100644 test-with-jest/.gitignore create mode 100644 test-with-jest/README.md create mode 100644 test-with-jest/package.json create mode 100644 test-with-jest/sample.env create mode 100644 test-with-jest/scripts/create-resources.sh create mode 100644 test-with-jest/src/fakes/fake-in-mem-db.spec.ts rename {testrunner => test-with-jest}/src/mock-function/data/connect-to-cosmos.ts (100%) rename {testrunner-typescript/src/app => test-with-jest/src/mock-function}/data/fake-data.ts (100%) rename {testrunner => test-with-jest}/src/mock-function/data/model.ts (100%) rename {testrunner => test-with-jest}/src/mock-function/data/verify.ts (100%) rename {testrunner => test-with-jest}/src/mock-function/index.ts (100%) create mode 100644 test-with-jest/src/mock-function/lib/insert.spec.ts rename {testrunner => test-with-jest}/src/mock-function/lib/insert.ts (100%) create mode 100644 test-with-jest/src/test-boilerplate/boilerplate-with-mock.spec.ts create mode 100644 test-with-jest/src/test-boilerplate/boilerplate.spec.ts rename {testrunner => test-with-jest}/tsconfig.json (63%) create mode 100644 test-with-node-testrunner/README.md create mode 100644 test-with-node-testrunner/package.json rename {testrunner-sha/src/app => test-with-node-testrunner/src}/data/connect-to-cosmos.ts (100%) rename {testrunner/src/mock-function => test-with-node-testrunner/src}/data/fake-data.ts (91%) rename {testrunner-sha/src/app => test-with-node-testrunner/src}/data/model.ts (100%) rename {testrunner-sha/src/app => test-with-node-testrunner/src}/data/verify.ts (78%) rename {testrunner-sha/src/app => test-with-node-testrunner/src}/index.ts (69%) rename {testrunner-sha/src/app => test-with-node-testrunner/src}/lib/insert.ts (88%) rename {testrunner-sha/test/basics => test-with-node-testrunner/test}/01-spies.test.ts (100%) create mode 100644 test-with-node-testrunner/test/02-stubs.test.ts rename {testrunner-sha/test/test-boilerplate => test-with-node-testrunner/test}/boilerplate-with-mock.test.ts (100%) rename {testrunner-sha/test/test-boilerplate => test-with-node-testrunner/test}/boilerplate.test.ts (100%) rename {testrunner-sha/test/in-mem-db => test-with-node-testrunner/test}/fake-in-mem-db.test.ts (57%) rename {testrunner-typescript/src/test-app => test-with-node-testrunner/test}/insert.test.ts (89%) create mode 100644 test-with-node-testrunner/tsconfig.json create mode 100644 test-with-vitest/README.md create mode 100644 test-with-vitest/coverage/lcov-report/base.css create mode 100644 test-with-vitest/coverage/lcov-report/block-navigation.js create mode 100644 test-with-vitest/coverage/lcov-report/favicon.png create mode 100644 test-with-vitest/coverage/lcov-report/index.html create mode 100644 test-with-vitest/coverage/lcov-report/prettify.css create mode 100644 test-with-vitest/coverage/lcov-report/prettify.js create mode 100644 test-with-vitest/coverage/lcov-report/sort-arrow-sprite.png create mode 100644 test-with-vitest/coverage/lcov-report/sorter.js create mode 100644 test-with-vitest/coverage/lcov-report/src/data/connect-to-cosmos.ts.html create mode 100644 test-with-vitest/coverage/lcov-report/src/data/fake-data.ts.html create mode 100644 test-with-vitest/coverage/lcov-report/src/data/index.html create mode 100644 test-with-vitest/coverage/lcov-report/src/data/model.ts.html create mode 100644 test-with-vitest/coverage/lcov-report/src/data/verify.ts.html create mode 100644 test-with-vitest/coverage/lcov-report/src/index.html create mode 100644 test-with-vitest/coverage/lcov-report/src/index.ts.html create mode 100644 test-with-vitest/coverage/lcov-report/src/lib/index.html create mode 100644 test-with-vitest/coverage/lcov-report/src/lib/insert.ts.html create mode 100644 test-with-vitest/coverage/lcov.info create mode 100644 test-with-vitest/package.json rename {testrunner-typescript/src/app => test-with-vitest/src}/data/connect-to-cosmos.ts (100%) rename {testrunner-sha/src/app => test-with-vitest/src}/data/fake-data.ts (91%) rename {testrunner-typescript/src/app => test-with-vitest/src}/data/model.ts (100%) rename {testrunner-typescript/src/app => test-with-vitest/src}/data/verify.ts (78%) rename {testrunner-typescript/src/app => test-with-vitest/src}/index.ts (69%) rename {testrunner-typescript/src/app => test-with-vitest/src}/lib/insert.ts (88%) create mode 100644 test-with-vitest/tests/01-spies.test.ts create mode 100644 test-with-vitest/tests/02-stubs.test.ts create mode 100644 test-with-vitest/tests/boilerplate-with-mock.test.ts create mode 100644 test-with-vitest/tests/boilerplate.test.ts create mode 100644 test-with-vitest/tests/fake-in-mem-db.test.ts create mode 100644 test-with-vitest/tests/insert.test.ts create mode 100644 test-with-vitest/tsconfig.json create mode 100644 test-with-vitest/vitest.config.ts delete mode 100644 testrunner-sha/README.md delete mode 100644 testrunner-sha/package-lock.json delete mode 100644 testrunner-sha/package.json delete mode 100644 testrunner-sha/src/app/lib/insert.test.ts delete mode 100644 testrunner-sha/test/basics/02-stubs.test.ts delete mode 100644 testrunner-sha/test/test-boilerplate/boilerplate-with-mock.test-2.ts delete mode 100644 testrunner-sha/tsconfig.json delete mode 100644 testrunner-typescript/README.md delete mode 100644 testrunner-typescript/package-lock.json delete mode 100644 testrunner-typescript/package.json delete mode 100644 testrunner-typescript/src/test-app/boilerplate-with-mock.test-2.ts delete mode 100644 testrunner-typescript/src/test-basics/01-spies.test.ts delete mode 100644 testrunner-typescript/src/test-basics/02-stubs.test.ts delete mode 100644 testrunner-typescript/src/test-boilerplate/boilerplate-with-mock.test.ts delete mode 100644 testrunner-typescript/src/test-boilerplate/boilerplate.test.ts delete mode 100644 testrunner-typescript/src/test-fakes/fake-in-mem-db.test.ts delete mode 100644 testrunner-typescript/tsconfig.json delete mode 100644 testrunner/README.md delete mode 100644 testrunner/package-lock.json delete mode 100644 testrunner/package.json delete mode 100644 testrunner/src/basics/01-spies.test.ts delete mode 100644 testrunner/src/basics/02-stubs.test.ts delete mode 100644 testrunner/src/fakes/fake-in-mem-db.test.ts delete mode 100644 testrunner/src/mock-function/lib/insert.test.ts delete mode 100644 testrunner/src/test-boilerplate/boilerplate-with-mock.test-2.ts delete mode 100644 testrunner/src/test-boilerplate/boilerplate-with-mock.test.ts delete mode 100644 testrunner/src/test-boilerplate/boilerplate.test.ts diff --git a/.gitignore b/.gitignore index c3560a86..035139f4 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,7 @@ node_modules .env* dist +**/*.env +**/coverage/ + diff --git a/package-lock.json b/package-lock.json index 75866788..889cca19 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,8 +12,9 @@ "nodejs-intro", "nodejs-intro-esm", "nodejs-route", - "unit-testing", - "testrunner" + "test-with-node-testrunner", + "test-with-vitest", + "test-with-jest" ], "dependencies": { "prettier": "^3.1.0" @@ -742,6 +743,431 @@ "dev": true, "license": "MIT" }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.1.tgz", + "integrity": "sha512-kfYGy8IdzTGy+z0vFGvExZtxkFlA4zAxgKEahG9KE1ScBjpQnFsNOX8KTU5ojNru5ed5CVoJYXFtoxaq5nFbjQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.1.tgz", + "integrity": "sha512-dp+MshLYux6j/JjdqVLnMglQlFu+MuVeNrmT5nk6q07wNhCdSnB7QZj+7G8VMUGh1q+vj2Bq8kRsuyA00I/k+Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.1.tgz", + "integrity": "sha512-50tM0zCJW5kGqgG7fQ7IHvQOcAn9TKiVRuQ/lN0xR+T2lzEFvAi1ZcS8DiksFcEpf1t/GYOeOfCAgDHFpkiSmA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.1.tgz", + "integrity": "sha512-GCj6WfUtNldqUzYkN/ITtlhwQqGWu9S45vUXs7EIYf+7rCiiqH9bCloatO9VhxsL0Pji+PF4Lz2XXCES+Q8hDw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.1.tgz", + "integrity": "sha512-5hEZKPf+nQjYoSr/elb62U19/l1mZDdqidGfmFutVUjjUZrOazAtwK+Kr+3y0C/oeJfLlxo9fXb1w7L+P7E4FQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.1.tgz", + "integrity": "sha512-hxVnwL2Dqs3fM1IWq8Iezh0cX7ZGdVhbTfnOy5uURtao5OIVCEyj9xIzemDi7sRvKsuSdtCAhMKarxqtlyVyfA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.1.tgz", + "integrity": "sha512-1MrCZs0fZa2g8E+FUo2ipw6jw5qqQiH+tERoS5fAfKnRx6NXH31tXBKI3VpmLijLH6yriMZsxJtaXUyFt/8Y4A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.1.tgz", + "integrity": "sha512-0IZWLiTyz7nm0xuIs0q1Y3QWJC52R8aSXxe40VUxm6BB1RNmkODtW6LHvWRrGiICulcX7ZvyH6h5fqdLu4gkww==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.1.tgz", + "integrity": "sha512-NdKOhS4u7JhDKw9G3cY6sWqFcnLITn6SqivVArbzIaf3cemShqfLGHYMx8Xlm/lBit3/5d7kXvriTUGa5YViuQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.1.tgz", + "integrity": "sha512-jaN3dHi0/DDPelk0nLcXRm1q7DNJpjXy7yWaWvbfkPvI+7XNSc/lDOnCLN7gzsyzgu6qSAmgSvP9oXAhP973uQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.1.tgz", + "integrity": "sha512-OJykPaF4v8JidKNGz8c/q1lBO44sQNUQtq1KktJXdBLn1hPod5rE/Hko5ugKKZd+D2+o1a9MFGUEIUwO2YfgkQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.1.tgz", + "integrity": "sha512-nGfornQj4dzcq5Vp835oM/o21UMlXzn79KobKlcs3Wz9smwiifknLy4xDCLUU0BWp7b/houtdrgUz7nOGnfIYg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.1.tgz", + "integrity": "sha512-1osBbPEFYwIE5IVB/0g2X6i1qInZa1aIoj1TdL4AaAb55xIIgbg8Doq6a5BzYWgr+tEcDzYH67XVnTmUzL+nXg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.1.tgz", + "integrity": "sha512-/6VBJOwUf3TdTvJZ82qF3tbLuWsscd7/1w+D9LH0W/SqUgM5/JJD0lrJ1fVIfZsqB6RFmLCe0Xz3fmZc3WtyVg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.1.tgz", + "integrity": "sha512-nSut/Mx5gnilhcq2yIMLMe3Wl4FK5wx/o0QuuCLMtmJn+WeWYoEGDN1ipcN72g1WHsnIbxGXd4i/MF0gTcuAjQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.1.tgz", + "integrity": "sha512-cEECeLlJNfT8kZHqLarDBQso9a27o2Zd2AQ8USAEoGtejOrCYHNtKP8XQhMDJMtthdF4GBmjR2au3x1udADQQQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.1.tgz", + "integrity": "sha512-xbfUhu/gnvSEg+EGovRc+kjBAkrvtk38RlerAzQxvMzlB4fXpCFCeUAYzJvrnhFtdeyVCDANSjJvOvGYoeKzFA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.1.tgz", + "integrity": "sha512-O96poM2XGhLtpTh+s4+nP7YCCAfb4tJNRVZHfIE7dgmax+yMP2WgMd2OecBuaATHKTHsLWHQeuaxMRnCsH8+5g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.1.tgz", + "integrity": "sha512-X53z6uXip6KFXBQ+Krbx25XHV/NCbzryM6ehOAeAil7X7oa4XIq+394PWGnwaSQ2WRA0KI6PUO6hTO5zeF5ijA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.1.tgz", + "integrity": "sha512-Na9T3szbXezdzM/Kfs3GcRQNjHzM6GzFBeU1/6IV/npKP5ORtp9zbQjvkDJ47s6BCgaAZnnnu/cY1x342+MvZg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.1.tgz", + "integrity": "sha512-T3H78X2h1tszfRSf+txbt5aOp/e7TAz3ptVKu9Oyir3IAOFPGV6O9c2naym5TOriy1l0nNf6a4X5UXRZSGX/dw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.1.tgz", + "integrity": "sha512-2H3RUvcmULO7dIE5EWJH8eubZAI4xw54H1ilJnRNZdeo8dTADEZ21w6J22XBkXqGJbe0+wnNJtw3UXRoLJnFEg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.1.tgz", + "integrity": "sha512-GE7XvrdOzrb+yVKB9KsRMq+7a2U/K5Cf/8grVFRAGJmfADr/e/ODQ134RK2/eeHqYV5eQRFxb1hY7Nr15fv1NQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.1.tgz", + "integrity": "sha512-uOxSJCIcavSiT6UnBhBzE8wy3n0hOkJsBOzy7HDAuTDE++1DJMRRVCPGisULScHL+a/ZwdXPpXD3IyFKjA7K8A==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.1.tgz", + "integrity": "sha512-Y1EQdcfwMSeQN/ujR5VayLOJ1BHaK+ssyk0AEzPjC+t1lITgsnccPqFjb6V+LsTp/9Iov4ysfjxLaGJ9RPtkVg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -893,7 +1319,6 @@ "version": "8.4.1", "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-8.4.1.tgz", "integrity": "sha512-XQ3cU+Q8Uqmrbf2e0cIC/QN43sTBSC8KF12u29Mb47tWrt2hAgBXSgpZMj4Ao8Uk0iJcU99QsOCaIL8934obCg==", - "dev": true, "funding": [ { "type": "opencollective", @@ -995,6 +1420,109 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -1392,112 +1920,390 @@ "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", "dev": true, "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { + "version": "5.1.1-v1", + "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", + "integrity": "sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==", + "dev": true, + "dependencies": { + "eslint-scope": "5.1.1" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.35.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.35.0.tgz", + "integrity": "sha512-uYQ2WfPaqz5QtVgMxfN6NpLD+no0MYHDBywl7itPYd3K5TjjSghNKmX8ic9S8NU8w81NVhJv/XojcHptRly7qQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.35.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.35.0.tgz", + "integrity": "sha512-FtKddj9XZudurLhdJnBl9fl6BwCJ3ky8riCXjEw3/UIbjmIY58ppWwPEvU3fNu+W7FUsAsB1CdH+7EQE6CXAPA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.35.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.35.0.tgz", + "integrity": "sha512-Uk+GjOJR6CY844/q6r5DR/6lkPFOw0hjfOIzVx22THJXMxktXG6CbejseJFznU8vHcEBLpiXKY3/6xc+cBm65Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.35.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.35.0.tgz", + "integrity": "sha512-3IrHjfAS6Vkp+5bISNQnPogRAW5GAV1n+bNCrDwXmfMHbPl5EhTmWtfmwlJxFRUCBZ+tZ/OxDyU08aF6NI/N5Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.35.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.35.0.tgz", + "integrity": "sha512-sxjoD/6F9cDLSELuLNnY0fOrM9WA0KrM0vWm57XhrIMf5FGiN8D0l7fn+bpUeBSU7dCgPV2oX4zHAsAXyHFGcQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.35.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.35.0.tgz", + "integrity": "sha512-2mpHCeRuD1u/2kruUiHSsnjWtHjqVbzhBkNVQ1aVD63CcexKVcQGwJ2g5VphOd84GvxfSvnnlEyBtQCE5hxVVw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.35.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.35.0.tgz", + "integrity": "sha512-mrA0v3QMy6ZSvEuLs0dMxcO2LnaCONs1Z73GUDBHWbY8tFFocM6yl7YyMu7rz4zS81NDSqhrUuolyZXGi8TEqg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.35.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.35.0.tgz", + "integrity": "sha512-DnYhhzcvTAKNexIql8pFajr0PiDGrIsBYPRvCKlA5ixSS3uwo/CWNZxB09jhIapEIg945KOzcYEAGGSmTSpk7A==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.35.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.35.0.tgz", + "integrity": "sha512-uagpnH2M2g2b5iLsCTZ35CL1FgyuzzJQ8L9VtlJ+FckBXroTwNOaD0z0/UF+k5K3aNQjbm8LIVpxykUOQt1m/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.35.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.35.0.tgz", + "integrity": "sha512-XQxVOCd6VJeHQA/7YcqyV0/88N6ysSVzRjJ9I9UA/xXpEsjvAgDTgH3wQYz5bmr7SPtVK2TsP2fQ2N9L4ukoUg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", - "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.35.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.35.0.tgz", + "integrity": "sha512-5pMT5PzfgwcXEwOaSrqVsz/LvjDZt+vQ8RT/70yhPU06PTuq8WaHhfT1LW+cdD7mW6i/J5/XIkX/1tCAkh1W6g==", + "cpu": [ + "loong64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.35.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.35.0.tgz", + "integrity": "sha512-c+zkcvbhbXF98f4CtEIP1EBA/lCic5xB0lToneZYvMeKu5Kamq3O8gqrxiYYLzlZH6E3Aq+TSW86E4ay8iD8EA==", + "cpu": [ + "ppc64" + ], "dev": true, - "engines": { - "node": ">=6.0.0" - } + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.35.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.35.0.tgz", + "integrity": "sha512-s91fuAHdOwH/Tad2tzTtPX7UZyytHIRR6V4+2IGlV0Cej5rkG0R61SX4l4y9sh0JBibMiploZx3oHKPnQBKe4g==", + "cpu": [ + "riscv64" + ], "dev": true, "license": "MIT", - "engines": { - "node": ">=6.0.0" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.35.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.35.0.tgz", + "integrity": "sha512-hQRkPQPLYJZYGP+Hj4fR9dDBMIM7zrzJDWFEMPdTnTy95Ljnv0/4w/ixFw3pTBMEuuEuoqtBINYND4M7ujcuQw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.35.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.35.0.tgz", + "integrity": "sha512-Pim1T8rXOri+0HmV4CdKSGrqcBWX0d1HoPnQ0uw0bdp1aP5SdQVNBy8LjYncvnLgu3fnnCt17xjWGd4cqh8/hA==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { - "version": "5.1.1-v1", - "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", - "integrity": "sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==", + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.35.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.35.0.tgz", + "integrity": "sha512-QysqXzYiDvQWfUiTm8XmJNO2zm9yC9P/2Gkrwg2dH9cxotQzunBHYr6jk4SujCTqnfGxduOmQcI7c2ryuW8XVg==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "eslint-scope": "5.1.1" - } + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.35.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.35.0.tgz", + "integrity": "sha512-OUOlGqPkVJCdJETKOCEf1mw848ZyJ5w50/rZ/3IBQVdLfR5jk/6Sr5m3iO2tdPgwo0x7VcncYuOvMhBWZq8ayg==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.35.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.35.0.tgz", + "integrity": "sha512-2/lsgejMrtwQe44glq7AFFHLfJBPafpsTa6JvP2NGef/ifOa4KBoglVf7AKN7EV9o32evBPRqfg96fEHzWo5kw==", + "cpu": [ + "ia32" + ], "dev": true, - "engines": { - "node": ">= 8" - } + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.35.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.35.0.tgz", + "integrity": "sha512-PIQeY5XDkrOysbQblSW7v3l1MDZzkTEzAfTPkj5VAu3FW8fS4ynyLg2sINp0fp3SjZ8xkRYpLqoKcYqAkhU1dw==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] }, "node_modules/@rtsao/scc": { "version": "1.1.0", @@ -1837,7 +2643,168 @@ "balanced-match": "^1.0.0" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.26.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.26.1.tgz", + "integrity": "sha512-V4Urxa/XtSUroUrnI7q6yUTD3hDtfJ2jzVfeT3VK0ciizfK2q/zGC0iDh1lFMUZR8cImRrep6/q0xd/1ZGPQpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.26.1", + "@typescript-eslint/types": "8.26.1", + "@typescript-eslint/typescript-estree": "8.26.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.26.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.26.1.tgz", + "integrity": "sha512-AjOC3zfnxd6S4Eiy3jwktJPclqhFHNyd8L6Gycf9WUPoKZpgM5PjkxY1X7uSy61xVpiJDhhk7XT2NVsN3ALTWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.26.1", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, + "node_modules/@vitest/coverage-istanbul": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@vitest/coverage-istanbul/-/coverage-istanbul-3.0.8.tgz", + "integrity": "sha512-v/frNs3RF//gQP/+AkXG2Bk51qiK1bGRubq/vgM7CxEw40Jl3N9rMpgAOAz8ELL9HAWvAZ9fswR8YyHhO1HxSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@istanbuljs/schema": "^0.1.3", + "debug": "^4.4.0", + "istanbul-lib-coverage": "^3.2.2", + "istanbul-lib-instrument": "^6.0.3", + "istanbul-lib-report": "^3.0.1", + "istanbul-lib-source-maps": "^5.0.6", + "istanbul-reports": "^3.1.7", + "magicast": "^0.3.5", + "test-exclude": "^7.0.1", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "vitest": "3.0.8" + } + }, + "node_modules/@vitest/coverage-istanbul/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@vitest/coverage-istanbul/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@vitest/coverage-istanbul/node_modules/istanbul-lib-source-maps": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", + "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.23", + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@vitest/coverage-istanbul/node_modules/minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", @@ -1853,79 +2820,133 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "node_modules/@vitest/coverage-istanbul/node_modules/test-exclude": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.1.tgz", + "integrity": "sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==", "dev": true, "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^10.4.1", + "minimatch": "^9.0.4" }, "engines": { - "node": ">=10" + "node": ">=18" } }, - "node_modules/@typescript-eslint/utils": { - "version": "8.26.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.26.1.tgz", - "integrity": "sha512-V4Urxa/XtSUroUrnI7q6yUTD3hDtfJ2jzVfeT3VK0ciizfK2q/zGC0iDh1lFMUZR8cImRrep6/q0xd/1ZGPQpg==", + "node_modules/@vitest/expect": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.0.8.tgz", + "integrity": "sha512-Xu6TTIavTvSSS6LZaA3EebWFr6tsoXPetOWNMOlc7LO88QVVBwq2oQWBoDiLCN6YTvNYsGSjqOO8CAdjom5DCQ==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.26.1", - "@typescript-eslint/types": "8.26.1", - "@typescript-eslint/typescript-estree": "8.26.1" + "@vitest/spy": "3.0.8", + "@vitest/utils": "3.0.8", + "chai": "^5.2.0", + "tinyrainbow": "^2.0.0" }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.0.8.tgz", + "integrity": "sha512-n3LjS7fcW1BCoF+zWZxG7/5XvuYH+lsFg+BDwwAz0arIwHQJFUEsKBQ0BLU49fCxuM/2HSeBPHQD8WjgrxMfow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "3.0.8", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.17" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" + "msw": "^2.4.9", + "vite": "^5.0.0 || ^6.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } } }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.26.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.26.1.tgz", - "integrity": "sha512-AjOC3zfnxd6S4Eiy3jwktJPclqhFHNyd8L6Gycf9WUPoKZpgM5PjkxY1X7uSy61xVpiJDhhk7XT2NVsN3ALTWg==", + "node_modules/@vitest/pretty-format": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.8.tgz", + "integrity": "sha512-BNqwbEyitFhzYMYHUVbIvepOyeQOSFA/NeJMIP9enMntkkxLgOcgABH6fjyXG85ipTgvero6noreavGIqfJcIg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.26.1", - "eslint-visitor-keys": "^4.2.0" + "tinyrainbow": "^2.0.0" }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.0.8.tgz", + "integrity": "sha512-c7UUw6gEcOzI8fih+uaAXS5DwjlBaCJUo7KJ4VvJcjL95+DSR1kova2hFuRt3w41KZEFcOEiq098KkyrjXeM5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "3.0.8", + "pathe": "^2.0.3" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "url": "https://opencollective.com/vitest" } }, - "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "node_modules/@vitest/snapshot": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.0.8.tgz", + "integrity": "sha512-x8IlMGSEMugakInj44nUrLSILh/zy1f2/BgH0UeHpNyOocG18M9CWVIFBaXPt8TrqVZWmcPjwfG/ht5tnpba8A==", "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.0.8", + "magic-string": "^0.30.17", + "pathe": "^2.0.3" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://opencollective.com/vitest" } }, - "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "dev": true + "node_modules/@vitest/spy": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.0.8.tgz", + "integrity": "sha512-MR+PzJa+22vFKYb934CejhR4BeRpMSoxkvNoDit68GQxRLSf11aT6CTj3XaqUU9rxgWJFnqicN/wxw6yBRkI1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^3.0.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.0.8.tgz", + "integrity": "sha512-nkBC3aEhfX2PdtQI/QwAWp8qZWwzASsU4Npbcd5RdMPBSSLCpkZp52P3xku3s3uA0HIEhGvEcF8rNkBsz9dQ4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.0.8", + "loupe": "^3.1.3", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } }, "node_modules/acorn": { "version": "8.14.1", @@ -2165,6 +3186,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "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/async-function": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", @@ -2407,6 +3438,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/call-bind": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", @@ -2497,6 +3538,23 @@ ], "license": "CC-BY-4.0" }, + "node_modules/chai": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.2.0.tgz", + "integrity": "sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -2524,6 +3582,16 @@ "node": ">=10" } }, + "node_modules/check-error": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", + "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + } + }, "node_modules/ci-info": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", @@ -2719,11 +3787,12 @@ } }, "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -2749,6 +3818,16 @@ } } }, + "node_modules/deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -2899,6 +3978,13 @@ "node": ">= 0.4" } }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", @@ -3031,6 +4117,13 @@ "node": ">= 0.4" } }, + "node_modules/es-module-lexer": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz", + "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==", + "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", @@ -3087,6 +4180,47 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/esbuild": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.1.tgz", + "integrity": "sha512-BGO5LtrGC7vxnqucAe/rmvKdJllfGaYWdyABvyMoXQlfYMb2bbRuReWR5tEGE//4LcNJj9XrkovTqNYRFZHAMQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.1", + "@esbuild/android-arm": "0.25.1", + "@esbuild/android-arm64": "0.25.1", + "@esbuild/android-x64": "0.25.1", + "@esbuild/darwin-arm64": "0.25.1", + "@esbuild/darwin-x64": "0.25.1", + "@esbuild/freebsd-arm64": "0.25.1", + "@esbuild/freebsd-x64": "0.25.1", + "@esbuild/linux-arm": "0.25.1", + "@esbuild/linux-arm64": "0.25.1", + "@esbuild/linux-ia32": "0.25.1", + "@esbuild/linux-loong64": "0.25.1", + "@esbuild/linux-mips64el": "0.25.1", + "@esbuild/linux-ppc64": "0.25.1", + "@esbuild/linux-riscv64": "0.25.1", + "@esbuild/linux-s390x": "0.25.1", + "@esbuild/linux-x64": "0.25.1", + "@esbuild/netbsd-arm64": "0.25.1", + "@esbuild/netbsd-x64": "0.25.1", + "@esbuild/openbsd-arm64": "0.25.1", + "@esbuild/openbsd-x64": "0.25.1", + "@esbuild/sunos-x64": "0.25.1", + "@esbuild/win32-arm64": "0.25.1", + "@esbuild/win32-ia32": "0.25.1", + "@esbuild/win32-x64": "0.25.1" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -3492,6 +4626,16 @@ "node": ">=4.0" } }, + "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/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -3560,6 +4704,16 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/expect-type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.0.tgz", + "integrity": "sha512-80F22aiJ3GLyVnS/B3HzgR6RelZVumzj9jkL0Rhz4h0xYbNW9PjlQz5h3J/SShErbXBc295vseR4/MIbVmUbeA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -3726,6 +4880,36 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/formdata-polyfill": { "version": "4.0.10", "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", @@ -4792,6 +5976,22 @@ "node": ">=8" } }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, "node_modules/jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", @@ -5651,6 +6851,13 @@ "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", "license": "MIT" }, + "node_modules/loupe": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.3.tgz", + "integrity": "sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==", + "dev": true, + "license": "MIT" + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -5661,6 +6868,28 @@ "yallist": "^3.0.2" } }, + "node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/magicast": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz", + "integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.25.4", + "@babel/types": "^7.25.4", + "source-map-js": "^1.2.0" + } + }, "node_modules/make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", @@ -5773,10 +7002,40 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "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/nanoid": { + "version": "3.3.9", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.9.tgz", + "integrity": "sha512-SppoicMGpZvbF1l3z4x7No3OlIjP7QJvC9XR7AhZr1kL133KHnKPztkKDc+Ir4aJ/1VhTySrtKhrsycmrMQfvg==", + "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/natural-compare": { "version": "1.4.0", @@ -6114,6 +7373,13 @@ "node": ">=6" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -6178,6 +7444,47 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/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==", + "dev": true, + "license": "ISC" + }, + "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/pathval": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", + "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.16" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -6284,7 +7591,36 @@ "dev": true, "license": "MIT", "engines": { - "node": ">= 0.4" + "node": ">= 0.4" + } + }, + "node_modules/postcss": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", + "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", + "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.8", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" } }, "node_modules/prelude-ls": { @@ -6549,6 +7885,45 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/rollup": { + "version": "4.35.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.35.0.tgz", + "integrity": "sha512-kg6oI4g+vc41vePJyO6dHt/yl0Rz3Thv0kJeVQ3D1kS3E5XSuKbPc29G4IpT/Kv1KQwgHVcN+HtyS+HYLNSvQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.6" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.35.0", + "@rollup/rollup-android-arm64": "4.35.0", + "@rollup/rollup-darwin-arm64": "4.35.0", + "@rollup/rollup-darwin-x64": "4.35.0", + "@rollup/rollup-freebsd-arm64": "4.35.0", + "@rollup/rollup-freebsd-x64": "4.35.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.35.0", + "@rollup/rollup-linux-arm-musleabihf": "4.35.0", + "@rollup/rollup-linux-arm64-gnu": "4.35.0", + "@rollup/rollup-linux-arm64-musl": "4.35.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.35.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.35.0", + "@rollup/rollup-linux-riscv64-gnu": "4.35.0", + "@rollup/rollup-linux-s390x-gnu": "4.35.0", + "@rollup/rollup-linux-x64-gnu": "4.35.0", + "@rollup/rollup-linux-x64-musl": "4.35.0", + "@rollup/rollup-win32-arm64-msvc": "4.35.0", + "@rollup/rollup-win32-ia32-msvc": "4.35.0", + "@rollup/rollup-win32-x64-msvc": "4.35.0", + "fsevents": "~2.3.2" + } + }, "node_modules/run-applescript": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz", @@ -6822,6 +8197,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/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", @@ -6856,6 +8238,16 @@ "node": ">=0.10.0" } }, + "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/source-map-support": { "version": "0.5.13", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", @@ -6887,6 +8279,20 @@ "node": ">=10" } }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.8.1.tgz", + "integrity": "sha512-vj5lIj3Mwf9D79hBkltk5qmkFI+biIKWS2IBxEyEU3AX1tUf7AoL8nSazCOiiqQsGKIq01SClsKEzweu34uwvA==", + "dev": true, + "license": "MIT" + }, "node_modules/stoppable": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz", @@ -6926,6 +8332,22 @@ "node": ">=8" } }, + "node_modules/string-width-cjs": { + "name": "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==", + "dev": true, + "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/string.prototype.trim": { "version": "1.2.10", "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", @@ -6997,6 +8419,20 @@ "node": ">=8" } }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-bom": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", @@ -7069,8 +8505,16 @@ "node": ">=8" } }, - "node_modules/testrunner": { - "resolved": "testrunner", + "node_modules/test-with-jest": { + "resolved": "test-with-jest", + "link": true + }, + "node_modules/test-with-node-testrunner": { + "resolved": "test-with-node-testrunner", + "link": true + }, + "node_modules/test-with-vitest": { + "resolved": "test-with-vitest", "link": true }, "node_modules/text-table": { @@ -7079,6 +8523,50 @@ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, + "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": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinypool": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz", + "integrity": "sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, + "node_modules/tinyrainbow": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", + "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", + "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -7306,10 +8794,6 @@ "dev": true, "license": "MIT" }, - "node_modules/unit-testing": { - "resolved": "unit-testing", - "link": true - }, "node_modules/update-browserslist-db": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", @@ -7378,6 +8862,171 @@ "node": ">=10.12.0" } }, + "node_modules/vite": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.2.tgz", + "integrity": "sha512-yW7PeMM+LkDzc7CgJuRLMW2Jz0FxMOsVJ8Lv3gpgW9WLcb9cTW+121UEr1hvmfR7w3SegR5ItvYyzVz1vxNJgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "postcss": "^8.5.3", + "rollup": "^4.30.1" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "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": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.0.8.tgz", + "integrity": "sha512-6PhR4H9VGlcwXZ+KWCdMqbtG649xCPZqfI9j2PsK1FcXgEzro5bGHcVKFCTqPLaNKZES8Evqv4LwvZARsq5qlg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.4.0", + "es-module-lexer": "^1.6.0", + "pathe": "^2.0.3", + "vite": "^5.0.0 || ^6.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vitest": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.0.8.tgz", + "integrity": "sha512-dfqAsNqRGUc8hB9OVR2P0w8PZPEckti2+5rdZip0WIz9WW0MnImJ8XiR61QhqLa92EQzKP2uPkzenKOAHyEIbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "3.0.8", + "@vitest/mocker": "3.0.8", + "@vitest/pretty-format": "^3.0.8", + "@vitest/runner": "3.0.8", + "@vitest/snapshot": "3.0.8", + "@vitest/spy": "3.0.8", + "@vitest/utils": "3.0.8", + "chai": "^5.2.0", + "debug": "^4.4.0", + "expect-type": "^1.1.0", + "magic-string": "^0.30.17", + "pathe": "^2.0.3", + "std-env": "^3.8.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.2", + "tinypool": "^1.0.2", + "tinyrainbow": "^2.0.0", + "vite": "^5.0.0 || ^6.0.0", + "vite-node": "3.0.8", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/debug": "^4.1.12", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@vitest/browser": "3.0.8", + "@vitest/ui": "3.0.8", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/debug": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, "node_modules/walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", @@ -7501,6 +9150,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "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/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -7519,6 +9185,25 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/wrap-ansi-cjs": { + "name": "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==", + "dev": true, + "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", @@ -7983,29 +9668,32 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "testrunner": { + "test-with-jest": { "version": "1.0.0", "license": "ISC", "dependencies": { "@azure/cosmos": "^4.1.0", "@azure/identity": "^4.4.1", + "@faker-js/faker": "^8.4.1", "dotenv": "^16.4.5", "uuid": "^10.0.0" }, "devDependencies": { - "@faker-js/faker": "^8.4.1", + "@types/jest": "^29.5.12", "@types/node": "^22.4.0", "@types/uuid": "^10.0.0", "@typescript-eslint/eslint-plugin": "^8.2.0", "eslint": "^8.56.0", "eslint-config-airbnb-base": "^15.0.0", "eslint-plugin-import": "^2.29.1", + "eslint-plugin-jest": "^28.8.0", "husky": "^9.0.10", + "jest": "^29.7.0", "prettier": "^3.2.4", "typescript": "^5.5.4" } }, - "testrunner/node_modules/prettier": { + "test-with-jest/node_modules/prettier": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", @@ -8021,8 +9709,52 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "test-with-node-testrunner": { + "version": "1.0.0", + "dependencies": { + "@azure/cosmos": "^4.1.0", + "@azure/identity": "^4.4.1", + "@faker-js/faker": "^8.4.1", + "dotenv": "^16.4.5", + "uuid": "^10.0.0" + }, + "devDependencies": { + "@types/node": "^22.13.10" + } + }, + "test-with-vitest": { + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@azure/cosmos": "^4.1.0", + "@azure/identity": "^4.4.1", + "@faker-js/faker": "^8.4.1", + "dotenv": "^16.4.5", + "uuid": "^10.0.0" + }, + "devDependencies": { + "@vitest/coverage-istanbul": "^3.0.8", + "vitest": "^3.0.8" + } + }, + "testrunner": { + "version": "1.0.0", + "extraneous": true, + "dependencies": { + "@faker-js/faker": "^9.6.0", + "@types/node": "^22.13.10" + } + }, + "testrunner-typescript": { + "version": "1.0.0", + "extraneous": true, + "dependencies": { + "@types/node": "^22.13.10" + } + }, "unit-testing": { "version": "1.0.0", + "extraneous": true, "license": "ISC", "dependencies": { "@azure/cosmos": "^4.1.0", @@ -8045,22 +9777,6 @@ "prettier": "^3.2.4", "typescript": "^5.5.4" } - }, - "unit-testing/node_modules/prettier": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", - "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", - "dev": true, - "license": "MIT", - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } } } } diff --git a/package.json b/package.json index 63ff9efb..e4529ab6 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,12 @@ "nodejs-intro", "nodejs-intro-esm", "nodejs-route", - "unit-testing" - ] + "test-with-node-testrunner", + "test-with-vitest", + "test-with-jest" + ], + "scripts":{ + "build": "npm run build --workspaces", + "test": "npm run test --workspaces" + } } diff --git a/test-with-jest/.editorconfig b/test-with-jest/.editorconfig new file mode 100644 index 00000000..5688c0d1 --- /dev/null +++ b/test-with-jest/.editorconfig @@ -0,0 +1,10 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = auto +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true +quote_type = single \ No newline at end of file diff --git a/test-with-jest/.eslintrc.json b/test-with-jest/.eslintrc.json new file mode 100644 index 00000000..55e8766c --- /dev/null +++ b/test-with-jest/.eslintrc.json @@ -0,0 +1,29 @@ +{ + "env": { + "node": true + }, + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:@typescript-eslint/recommended-requiring-type-checking" + ], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": 2021, + "project": "./tsconfig.json" + }, + "plugins": [ + "jest", + "@typescript-eslint" + ], + "rules": { + "no-console": "off", + "no-noImplicitAny": "off", + "require-await": "error", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-unsafe-assignment": "off", + "@typescript-eslint/no-unsafe-argument": "off", + "@typescript-eslint/no-unsafe-member-access": "off", + "@typescript-eslint/unbound-method": "off" + } +} \ No newline at end of file diff --git a/test-with-jest/.gitignore b/test-with-jest/.gitignore new file mode 100644 index 00000000..cecab175 --- /dev/null +++ b/test-with-jest/.gitignore @@ -0,0 +1,3 @@ +node_modules +.env* +dist \ No newline at end of file diff --git a/test-with-jest/README.md b/test-with-jest/README.md new file mode 100644 index 00000000..db5aeb98 --- /dev/null +++ b/test-with-jest/README.md @@ -0,0 +1,41 @@ +# Jest for the Azure SDK for JavaScript + +This subfolder is the source code for the [How to Test Azure SDK Integration in JavaScript Applications](https://learn.microsoft.com/azure/developer/javascript/sdk/test-sdk-integration). The purpose is to demonstrate unit test mocks for the Azure SDK for JavaScript. These specific examples use Azure Cosmos DB. + +## To run the test + +1. `npm install` +2. `npm run build` +3. `npm test` + + ```console + > test-with-jest@1.0.0 test + > jest --detectOpenHandles dist --coverage + + PASS dist/mock-function/lib/insert.spec.js (275.092 s) + PASS dist/fakes/fake-in-mem-db.spec.js + PASS dist/test-boilerplate/boilerplate-with-mock.spec.js + PASS dist/test-boilerplate/boilerplate.spec.js + ---------------|---------|----------|---------|---------|------------------- + File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s + ---------------|---------|----------|---------|---------|------------------- + All files | 76.47 | 55.55 | 86.66 | 80.43 | + data | 75 | 20 | 83.33 | 75 | + fake-data.js | 100 | 100 | 100 | 100 | + model.js | 50 | 20 | 66.66 | 50 | 13-22 + lib | 77.77 | 76.47 | 88.88 | 86.36 | + insert.js | 77.77 | 76.47 | 88.88 | 86.36 | 29-35 + ---------------|---------|----------|---------|---------|------------------- + + Test Suites: 4 passed, 4 total + Tests: 6 passed, 6 total + Snapshots: 0 total + Time: 285.194 s + Ran all test suites matching /dist/i. + ``` + +## Related content + +* [Passwordless connections for Azure services](https://learn.microsoft.com/azure/developer/intro/passwordless-overview) +* [Cosmos DB keyless access to the service](https://learn.microsoft.com/azure/cosmos-db/role-based-access-control) +* [Jest](https://jestjs.io/) \ No newline at end of file diff --git a/test-with-jest/package.json b/test-with-jest/package.json new file mode 100644 index 00000000..ccf2b14f --- /dev/null +++ b/test-with-jest/package.json @@ -0,0 +1,43 @@ +{ + "name": "test-with-jest", + "version": "1.0.0", + "main": "index.js", + "scripts": { + "clear": "rm -rf dist && rm -rf coverage", + "start": "node dist/mock-function/index.js", + "test": "jest --detectOpenHandles dist --coverage", + "build": "npm run clear && tsc", + "lint": "eslint \"./src/**/*.ts\" --fix", + "format": "prettier \"./src/**/*.ts\" --write", + "precommit": "npm run format && git add . && npm run lint" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "@azure/cosmos": "^4.1.0", + "@azure/identity": "^4.4.1", + "dotenv": "^16.4.5", + "uuid": "^10.0.0", + "@faker-js/faker": "^8.4.1" + }, + "devDependencies": { + "@types/jest": "^29.5.12", + "@types/node": "^22.4.0", + "@types/uuid": "^10.0.0", + "@typescript-eslint/eslint-plugin": "^8.2.0", + "eslint": "^8.56.0", + "eslint-config-airbnb-base": "^15.0.0", + "eslint-plugin-import": "^2.29.1", + "eslint-plugin-jest": "^28.8.0", + "husky": "^9.0.10", + "jest": "^29.7.0", + "prettier": "^3.2.4", + "typescript": "^5.5.4" + }, + "husky": { + "hooks": { + "pre-commit": "npm run precommit" + } + } +} diff --git a/test-with-jest/sample.env b/test-with-jest/sample.env new file mode 100644 index 00000000..e1da034c --- /dev/null +++ b/test-with-jest/sample.env @@ -0,0 +1,9 @@ +SUBSCRIPTION_ID= +RESOURCE_GROUP_NAME="my-resource-group +LOCATION=eastus2 +COSMOS_DATABASE_NAME="my-db" +COSMOS_CONTAINER_NAME="my-container" + +## ./scripts/create-resources.sh adds the COSMOS DB endpoint +# The endpoint URL for your Azure Cosmos DB account +#COSMOS_ENDPOINT= diff --git a/test-with-jest/scripts/create-resources.sh b/test-with-jest/scripts/create-resources.sh new file mode 100644 index 00000000..ff9ffe2f --- /dev/null +++ b/test-with-jest/scripts/create-resources.sh @@ -0,0 +1,55 @@ +#!/bin/bash + +# Prerequisites: +# Azure CLI installed +# `az login` + +# Read .env file in the script +set -a +source ../.env + +random_string() { + cat /dev/urandom | tr -dc 'a-z0-9' | fold -w 10 | head -n 1 +} +RANDOM_STRING=$(random_string) + +printf "Subscription ID: $SUBSCRIPTION_ID\n" +printf "Location: $LOCATION\n" +printf "Cosmos DB Database: $COSMOS_DATABASE_NAME\n" +printf "Cosmos DB Container name: $COSMOS_CONTAINER_NAME\n" + +RG="$RESOURCE_GROUP_NAME$RANDOM_STRING" +printf "Resource Group: $RG\n" + +RESOURCE_NAME="$COSMOS_DB_RESOURCE_NAME$RANDOM_STRING" +printf "Resource Name: $RESOURCE_NAME\n" + +# Create a resource group +az group create \ + --subscription $SUBSCRIPTION_ID \ + --name "$RG" \ + --location $LOCATION +printf "Resource Group created\n" + +# Create a Cosmos DB account +az cosmosdb create \ + --subscription $SUBSCRIPTION_ID \ + --resource-group $RG \ + --name $RESOURCE_NAME \ + --kind MongoDB \ + --locations regionName=$LOCATION failoverPriority=0 isZoneRedundant=False +printf "Cosmos DB account created\n" + +# Get the Cosmos DB account endpoint +COSMOS_DB_ENDPOINT=$(az cosmosdb show \ + --subscription $SUBSCRIPTION_ID \ + --resource-group $RG \ + --name $RESOURCE_NAME \ + --query "documentEndpoint" \ + --output tsv) +printf "Cosmos DB Endpoint: $COSMOS_DB_ENDPOINT\n" + +# Append the endpoint to the .env file +echo "COSMOS_DB_ENDPOINT=$COSMOS_DB_ENDPOINT" >> ../.env + +echo "Cosmos DB Endpoint: $COSMOS_DB_ENDPOINT" diff --git a/test-with-jest/src/fakes/fake-in-mem-db.spec.ts b/test-with-jest/src/fakes/fake-in-mem-db.spec.ts new file mode 100644 index 00000000..7c03a34c --- /dev/null +++ b/test-with-jest/src/fakes/fake-in-mem-db.spec.ts @@ -0,0 +1,61 @@ +// fake-in-mem-db.spec.ts +class FakeDatabase { + private data: Record; + + constructor() { + this.data = {}; + } + + save(key: string, value: any): void { + this.data[key] = value; + } + + get(key: string): any { + return this.data[key]; + } +} + +// Function to test +function someTestFunction(db: FakeDatabase, key: string, value: any): any { + db.save(key, value); + return db.get(key); +} + +// Jest test suite +describe('someTestFunction', () => { + let fakeDb: FakeDatabase; + let testKey: string; + let testValue: any; + + beforeEach(() => { + fakeDb = new FakeDatabase(); + testKey = 'testKey'; + testValue = { + first: 'John', + last: 'Jones', + lastUpdated: new Date().toISOString(), + }; + + // Spy on the save method + jest.spyOn(fakeDb, 'save'); + }); + + afterEach(() => { + // Clear all mocks + jest.clearAllMocks(); + }); + + test('should save and return the correct value', () => { + // Perform test + const result = someTestFunction(fakeDb, testKey, testValue); + + // Verify state + expect(result).toEqual(testValue); + expect(result.first).toBe('John'); + expect(result.last).toBe('Jones'); + expect(result.lastUpdated).toBe(testValue.lastUpdated); + + // Verify behavior + expect(fakeDb.save).toHaveBeenCalledWith(testKey, testValue); + }); +}); diff --git a/testrunner/src/mock-function/data/connect-to-cosmos.ts b/test-with-jest/src/mock-function/data/connect-to-cosmos.ts similarity index 100% rename from testrunner/src/mock-function/data/connect-to-cosmos.ts rename to test-with-jest/src/mock-function/data/connect-to-cosmos.ts diff --git a/testrunner-typescript/src/app/data/fake-data.ts b/test-with-jest/src/mock-function/data/fake-data.ts similarity index 100% rename from testrunner-typescript/src/app/data/fake-data.ts rename to test-with-jest/src/mock-function/data/fake-data.ts diff --git a/testrunner/src/mock-function/data/model.ts b/test-with-jest/src/mock-function/data/model.ts similarity index 100% rename from testrunner/src/mock-function/data/model.ts rename to test-with-jest/src/mock-function/data/model.ts diff --git a/testrunner/src/mock-function/data/verify.ts b/test-with-jest/src/mock-function/data/verify.ts similarity index 100% rename from testrunner/src/mock-function/data/verify.ts rename to test-with-jest/src/mock-function/data/verify.ts diff --git a/testrunner/src/mock-function/index.ts b/test-with-jest/src/mock-function/index.ts similarity index 100% rename from testrunner/src/mock-function/index.ts rename to test-with-jest/src/mock-function/index.ts diff --git a/test-with-jest/src/mock-function/lib/insert.spec.ts b/test-with-jest/src/mock-function/lib/insert.spec.ts new file mode 100644 index 00000000..a7d206d7 --- /dev/null +++ b/test-with-jest/src/mock-function/lib/insert.spec.ts @@ -0,0 +1,123 @@ +// insertDocument.test.ts +import { Container } from '../data/connect-to-cosmos'; +import { createTestInputAndResult } from '../data/fake-data'; +import { + DbDocument, + DbError, + isDbError, + isVerificationErrors, + RawInput, +} from '../data/model'; +import { inputVerified } from '../data/verify'; +import { insertDocument } from './insert'; + +// Mock app dependencies for Cosmos DB setup +jest.mock('../data/connect-to-cosmos', () => ({ + connectToContainer: jest.fn(), + getUniqueId: jest.fn().mockReturnValue('unique-id'), +})); + +// Mock app dependencies for input verification +jest.mock('../data/verify', () => ({ + inputVerified: jest.fn(), +})); + +describe('SDK', () => { + // Mock the Cosmo DB Container object + let mockContainer: jest.Mocked; + + beforeEach(() => { + // Clear all mocks before each test + jest.clearAllMocks(); + + // Mock the Cosmos DB Container create method + mockContainer = { + items: { + create: jest.fn(), + }, + } as unknown as jest.Mocked; + }); + + it('should return verification error if input is not verified', async () => { + // Arrange - Mock the input verification function to return false + jest.mocked(inputVerified).mockReturnValue(false); + + // Arrange - wrong shape of doc on purpose + const doc = { name: 'test' }; + + // Act - Call the function to test + const insertDocumentResult = await insertDocument( + mockContainer, + doc as unknown as RawInput, + ); + + // Assert - State verification: Check the result when verification fails + if (isVerificationErrors(insertDocumentResult)) { + expect(insertDocumentResult).toEqual({ + message: 'Verification failed', + }); + } else { + throw new Error('Result is not of type VerificationErrors'); + } + + // Assert - Behavior verification: Ensure create method was not called + expect(mockContainer.items.create).not.toHaveBeenCalled(); + }); + + it('should insert document successfully', async () => { + // Arrange - create input and expected result data + const { input, result }: { input: RawInput; result: Partial } = + createTestInputAndResult(); + + // Arrange - mock the input verification function to return true + (inputVerified as jest.Mock).mockReturnValue(true); + (mockContainer.items.create as jest.Mock).mockResolvedValue({ + resource: result, + }); + + // Act - Call the function to test + const insertDocumentResult = await insertDocument(mockContainer, input); + + // Assert - State verification: Check the result when insertion is successful + expect(insertDocumentResult).toEqual(result); + + // Assert - Behavior verification: Ensure create method was called with correct arguments + expect(mockContainer.items.create).toHaveBeenCalledTimes(1); + expect(mockContainer.items.create).toHaveBeenCalledWith({ + id: input.id, + name: result.name, + }); + }); + + it('should return error if db insert fails', async () => { + // Arrange - create input and expected result data + const { input, result } = createTestInputAndResult(); + + // Arrange - mock the input verification function to return true + jest.mocked(inputVerified).mockReturnValue(true); + + // Arrange - mock the Cosmos DB create method to throw an error + const mockError: DbError = { + message: 'An unknown error occurred', + code: 500, + }; + jest.mocked(mockContainer.items.create).mockRejectedValue(mockError); + + // Act - Call the function to test + const insertDocumentResult = await insertDocument(mockContainer, input); + + // Assert - verify type as DbError + if (isDbError(insertDocumentResult)) { + expect(insertDocumentResult.message).toBe(mockError.message); + } else { + throw new Error('Result is not of type DbError'); + } + + // Assert - Behavior verification: Ensure create method was called with correct arguments + expect(mockContainer.items.create).toHaveBeenCalledTimes(1); + expect(mockContainer.items.create).toHaveBeenCalledWith({ + id: input.id, + name: result.name, + }); + }); +}); diff --git a/testrunner/src/mock-function/lib/insert.ts b/test-with-jest/src/mock-function/lib/insert.ts similarity index 100% rename from testrunner/src/mock-function/lib/insert.ts rename to test-with-jest/src/mock-function/lib/insert.ts diff --git a/test-with-jest/src/test-boilerplate/boilerplate-with-mock.spec.ts b/test-with-jest/src/test-boilerplate/boilerplate-with-mock.spec.ts new file mode 100644 index 00000000..20c90818 --- /dev/null +++ b/test-with-jest/src/test-boilerplate/boilerplate-with-mock.spec.ts @@ -0,0 +1,25 @@ +// boilerplate-with-mock.spec.ts + +// Mock the dependencies +jest.mock('../mock-function/data/connect-to-cosmos', () => ({ + myFunctionToMock: jest.fn(), +})); + +describe('nameOfGroupOfTests', () => { + beforeEach(() => { + // Clear all mocks before each test + jest.clearAllMocks(); + + // Other setup required before each test + }); + + it('should if ', async () => { + // Arrange + // - set up the test data and the expected result + // Act + // - call the function to test + // Assert + // - check the state: result returned from function + // - check the behavior: dependency function calls + }); +}); diff --git a/test-with-jest/src/test-boilerplate/boilerplate.spec.ts b/test-with-jest/src/test-boilerplate/boilerplate.spec.ts new file mode 100644 index 00000000..8de59ded --- /dev/null +++ b/test-with-jest/src/test-boilerplate/boilerplate.spec.ts @@ -0,0 +1,20 @@ +// boilerplate.spec.ts + +describe('nameOfGroupOfTests', () => { + beforeEach(() => { + // Setup required before each test + }); + afterEach(() => { + // Cleanup required after each test + }); + + it('should if ', async () => { + // Arrange + // - set up the test data and the expected result + // Act + // - call the function to test + // Assert + // - check the state: result returned from function + // - check the behavior: dependency function calls + }); +}); diff --git a/testrunner/tsconfig.json b/test-with-jest/tsconfig.json similarity index 63% rename from testrunner/tsconfig.json rename to test-with-jest/tsconfig.json index eaff57f9..9f817d53 100644 --- a/testrunner/tsconfig.json +++ b/test-with-jest/tsconfig.json @@ -4,16 +4,9 @@ "module": "commonjs", // Specify module code generation "strict": true, // Enable all strict type-checking options "esModuleInterop": true, // Enables emit interoperability between CommonJS and ES Modules - "noImplicitAny": false, // Enable error reporting for expressions and declarations with an implied 'any' type "forceConsistentCasingInFileNames": true, // Disallow inconsistently-cased references to the same file - "outDir": "./dist", - "rootDir": "./", + "types": ["jest", "node"], // Specify type package + "outDir": "dist", + "rootDir": "src", }, - "include": [ - "src", - "test" - ], - "exclude": [ - "node_modules" - ] } \ No newline at end of file diff --git a/test-with-node-testrunner/README.md b/test-with-node-testrunner/README.md new file mode 100644 index 00000000..6b66e35c --- /dev/null +++ b/test-with-node-testrunner/README.md @@ -0,0 +1,73 @@ +# Node.js native test runner for the Azure SDK for JavaScript + +This subfolder is the source code for the [How to Test Azure SDK Integration in JavaScript Applications](https://learn.microsoft.com/azure/developer/javascript/sdk/test-sdk-integration). The purpose is to demonstrate unit test mocks for the Azure SDK for JavaScript. These specific examples use Azure Cosmos DB. + +## To run the test + +1. `npm install` +3. `npm test` + + ```console + > test-with-node-testrunner@1.0.0 build + > rm -rf dist && tsc + + ▶ Spies + ✔ should verify calls in a mock (2.466559ms) + ✔ Spies (3.917687ms) + ▶ Stub Test Suite + ✔ should stub APIs (3.878824ms) + ✔ should stub different values for API calls (1.967753ms) + ✔ Stub Test Suite (7.46516ms) + ▶ boilerplate with mock + ✔ should if (2.118992ms) + ✔ boilerplate with mock (3.480984ms) + ▶ boilerplate + ✔ should if (1.587994ms) + ✔ boilerplate (2.865063ms) + ▶ In-Mem DB + ✔ should save and return the correct value (3.486273ms) + ✔ In-Mem DB (4.931667ms) + ▶ SDK + ✔ should insert document successfully (6.777565ms) + ✔ should return verification error if input is not verified (2.334627ms) + ✔ should return error if db insert fails (0.827453ms) + ✔ SDK (11.135955ms) + ℹ tests 9 + ℹ suites 6 + ℹ pass 9 + ℹ fail 0 + ℹ cancelled 0 + ℹ skipped 0 + ℹ todo 0 + ℹ duration_ms 281634.635767 + ℹ start of coverage report + ℹ ------------------------------------------------------------------------------------------ + ℹ file | line % | branch % | funcs % | uncovered lines + ℹ ------------------------------------------------------------------------------------------ + ℹ dist | | | | + ℹ src | | | | + ℹ data | | | | + ℹ connect-to-cosmos.js | 36.96 | 100.00 | 0.00 | 4-10 21-25 27-41 43-44 + ℹ fake-data.js | 100.00 | 100.00 | 100.00 | + ℹ model.js | 47.83 | 100.00 | 66.67 | 12-23 + ℹ verify.js | 72.73 | 100.00 | 0.00 | 7-9 + ℹ lib | | | | + ℹ insert.js | 82.22 | 75.00 | 90.00 | 32-39 + ℹ test | | | | + ℹ 01-spies.test.js | 100.00 | 85.71 | 100.00 | + ℹ 02-stubs.test.js | 92.47 | 88.24 | 89.66 | 19-25 + ℹ boilerplate-with-mock.test.js | 90.48 | 84.21 | 81.25 | 21-24 + ℹ boilerplate.test.js | 100.00 | 90.00 | 72.73 | + ℹ fake-in-mem-db.test.js | 100.00 | 90.91 | 100.00 | + ℹ insert.test.js | 94.83 | 89.66 | 77.78 | 37 62 79-81 92 + ℹ ------------------------------------------------------------------------------------------ + ℹ all files | 86.44 | 87.05 | 80.51 | + ℹ ------------------------------------------------------------------------------------------ + ℹ end of coverage report + ``` + +## Related content + +* [Passwordless connections for Azure services](https://learn.microsoft.com/azure/developer/intro/passwordless-overview) +* [Cosmos DB keyless access to the service](https://learn.microsoft.com/azure/cosmos-db/role-based-access-control) +* [Node.js Test Runner](https://nodejs.org/docs/latest/api/test.html#test-runner) \ No newline at end of file diff --git a/test-with-node-testrunner/package.json b/test-with-node-testrunner/package.json new file mode 100644 index 00000000..c57547f0 --- /dev/null +++ b/test-with-node-testrunner/package.json @@ -0,0 +1,19 @@ +{ + "name": "test-with-node-testrunner", + "version": "1.0.0", + "main": "index.js", + "scripts": { + "build": "rm -rf dist && tsc", + "test": "npm run build && node --test --experimental-test-coverage --experimental-test-module-mocks --trace-exit" + }, + "devDependencies": { + "@types/node": "^22.13.10" + }, + "dependencies": { + "@azure/cosmos": "^4.1.0", + "@azure/identity": "^4.4.1", + "dotenv": "^16.4.5", + "uuid": "^10.0.0", + "@faker-js/faker": "^8.4.1" + } +} diff --git a/testrunner-sha/src/app/data/connect-to-cosmos.ts b/test-with-node-testrunner/src/data/connect-to-cosmos.ts similarity index 100% rename from testrunner-sha/src/app/data/connect-to-cosmos.ts rename to test-with-node-testrunner/src/data/connect-to-cosmos.ts diff --git a/testrunner/src/mock-function/data/fake-data.ts b/test-with-node-testrunner/src/data/fake-data.ts similarity index 91% rename from testrunner/src/mock-function/data/fake-data.ts rename to test-with-node-testrunner/src/data/fake-data.ts index 326912df..82c90b66 100644 --- a/testrunner/src/mock-function/data/fake-data.ts +++ b/test-with-node-testrunner/src/data/fake-data.ts @@ -1,6 +1,6 @@ import { v4 as uuidv4 } from 'uuid'; import { faker } from '@faker-js/faker'; -import { DbDocument, RawInput } from './model'; +import type { DbDocument, RawInput } from './model.js'; function createFixture(): T { const result = { diff --git a/testrunner-sha/src/app/data/model.ts b/test-with-node-testrunner/src/data/model.ts similarity index 100% rename from testrunner-sha/src/app/data/model.ts rename to test-with-node-testrunner/src/data/model.ts diff --git a/testrunner-sha/src/app/data/verify.ts b/test-with-node-testrunner/src/data/verify.ts similarity index 78% rename from testrunner-sha/src/app/data/verify.ts rename to test-with-node-testrunner/src/data/verify.ts index 1d507135..67cba7c6 100644 --- a/testrunner-sha/src/app/data/verify.ts +++ b/test-with-node-testrunner/src/data/verify.ts @@ -1,5 +1,5 @@ // input-verified.ts -import { validateRawInput } from './model.ts'; +import { validateRawInput } from './model.js'; export default class Verfiy { static inputVerified(doc: any): boolean { diff --git a/testrunner-sha/src/app/index.ts b/test-with-node-testrunner/src/index.ts similarity index 69% rename from testrunner-sha/src/app/index.ts rename to test-with-node-testrunner/src/index.ts index b75a8a00..cf9b01ac 100644 --- a/testrunner-sha/src/app/index.ts +++ b/test-with-node-testrunner/src/index.ts @@ -1,7 +1,7 @@ -import CosmosConnector from './data/connect-to-cosmos.ts'; -import { createTestInput } from './data/fake-data.ts'; -import type { DbDocument, DbError, VerificationErrors } from './data/model.ts'; -import { insertDocument } from './lib/insert.ts'; +import CosmosConnector from './data/connect-to-cosmos.js'; +import { createTestInput } from './data/fake-data.js'; +import type { DbDocument, DbError, VerificationErrors } from './data/model.js'; +import { insertDocument } from './lib/insert.js'; async function main(): Promise { const container = await CosmosConnector.connectToContainer(); diff --git a/testrunner-sha/src/app/lib/insert.ts b/test-with-node-testrunner/src/lib/insert.ts similarity index 88% rename from testrunner-sha/src/app/lib/insert.ts rename to test-with-node-testrunner/src/lib/insert.ts index 4549bc07..e873cadd 100644 --- a/testrunner-sha/src/app/lib/insert.ts +++ b/test-with-node-testrunner/src/lib/insert.ts @@ -1,12 +1,12 @@ // insertDocument.ts -import { Container } from '../data/connect-to-cosmos.ts'; +import { Container } from '../data/connect-to-cosmos.js'; import type { DbDocument, DbError, RawInput, VerificationErrors, -} from '../data/model.ts'; -import Verify from '../data/verify.ts'; +} from '../data/model.js'; +import Verify from '../data/verify.js'; export async function insertDocument( container: Container, diff --git a/testrunner-sha/test/basics/01-spies.test.ts b/test-with-node-testrunner/test/01-spies.test.ts similarity index 100% rename from testrunner-sha/test/basics/01-spies.test.ts rename to test-with-node-testrunner/test/01-spies.test.ts diff --git a/test-with-node-testrunner/test/02-stubs.test.ts b/test-with-node-testrunner/test/02-stubs.test.ts new file mode 100644 index 00000000..883920a4 --- /dev/null +++ b/test-with-node-testrunner/test/02-stubs.test.ts @@ -0,0 +1,85 @@ +import { describe, it, beforeEach, mock } from 'node:test'; +import assert from 'node:assert'; + +class Service { + static async getTalks({ skip, limit }) { + // Use a public API (jsonplaceholder) to retrieve posts with pagination. + const url = `https://jsonplaceholder.typicode.com/posts?_start=${skip}&_limit=${limit}`; + const response = await fetch(url); + return response.json(); + } +} + +function mapResponse(data) { + return data + .map(({ id, title }, index) => `[${index}] id: ${id}, title: ${title}`) + .join('\n'); +} + +async function run({ skip = 0, limit = 10 } = {}) { + const talks = mapResponse(await Service.getTalks({ skip, limit })); + return talks; +} + +describe('Stub Test Suite', () => { + beforeEach(() => mock.restoreAll()); + + it('should stub APIs', async () => { + // Stub Service.getTalks so that it returns one fake post. + const m = mock.method(Service, 'getTalks').mock; + m.mockImplementation(async () => [ + { + id: '1', + title: 'Sample Post' + } + ]); + + const result = await run({ limit: 1 }); + const expected = `[0] id: 1, title: Sample Post`; + + // Verify that the stubbed method was called once. + assert.deepStrictEqual(m.callCount(), 1); + const calls = m.calls; + assert.deepStrictEqual(calls[0].arguments[0], { skip: 0, limit: 1 }); + assert.strictEqual(result, expected); + }); + + it('should stub different values for API calls', async () => { + + // Instead of chaining mockImplementationOnce, build a responses array. + const m = mock.method(Service, 'getTalks').mock; + const responses = [ + async () => [{ id: '1', title: 'Post One' }], + async () => [{ id: '2', title: 'Post Two' }], + async () => [{ id: '3', title: 'Post Three' }] + ]; + + m.mockImplementation(async (args) => { + // Return the next response from the array. + const fn = responses.shift(); + return fn ? fn() : []; + }); + + { + const result = await run({ skip: 0, limit: 1 }); + const expected = `[0] id: 1, title: Post One`; + assert.strictEqual(result, expected); + } + { + const result = await run({ skip: 1, limit: 1 }); + const expected = `[0] id: 2, title: Post Two`; + assert.strictEqual(result, expected); + } + { + const result = await run({ skip: 2, limit: 1 }); + const expected = `[0] id: 3, title: Post Three`; + assert.strictEqual(result, expected); + } + + const calls = m.calls; + assert.strictEqual(m.callCount(), 3); + assert.deepStrictEqual(calls[0].arguments[0], { skip: 0, limit: 1 }); + assert.deepStrictEqual(calls[1].arguments[0], { skip: 1, limit: 1 }); + assert.deepStrictEqual(calls[2].arguments[0], { skip: 2, limit: 1 }); + }); +}); diff --git a/testrunner-sha/test/test-boilerplate/boilerplate-with-mock.test.ts b/test-with-node-testrunner/test/boilerplate-with-mock.test.ts similarity index 100% rename from testrunner-sha/test/test-boilerplate/boilerplate-with-mock.test.ts rename to test-with-node-testrunner/test/boilerplate-with-mock.test.ts diff --git a/testrunner-sha/test/test-boilerplate/boilerplate.test.ts b/test-with-node-testrunner/test/boilerplate.test.ts similarity index 100% rename from testrunner-sha/test/test-boilerplate/boilerplate.test.ts rename to test-with-node-testrunner/test/boilerplate.test.ts diff --git a/testrunner-sha/test/in-mem-db/fake-in-mem-db.test.ts b/test-with-node-testrunner/test/fake-in-mem-db.test.ts similarity index 57% rename from testrunner-sha/test/in-mem-db/fake-in-mem-db.test.ts rename to test-with-node-testrunner/test/fake-in-mem-db.test.ts index 103000d6..7bdc80c9 100644 --- a/testrunner-sha/test/in-mem-db/fake-in-mem-db.test.ts +++ b/test-with-node-testrunner/test/fake-in-mem-db.test.ts @@ -1,5 +1,4 @@ -// fake-in-mem-db.spec.ts -import { describe, it, beforeEach, afterEach } from 'node:test'; +import { describe, it, beforeEach, afterEach, mock } from 'node:test'; import assert from 'node:assert'; class FakeDatabase { @@ -28,9 +27,6 @@ describe('In-Mem DB', () => { let fakeDb: FakeDatabase; let testKey: string; let testValue: any; - let originalSave: (key: string, value: any) => void; - let saveSpyCallCount: number; - let saveSpyArgs: Array<[string, any]>; beforeEach(() => { fakeDb = new FakeDatabase(); @@ -40,35 +36,29 @@ describe('In-Mem DB', () => { last: 'Jones', lastUpdated: new Date().toISOString(), }; - - // Create a simple spy on the save method - originalSave = fakeDb.save.bind(fakeDb); - saveSpyCallCount = 0; - saveSpyArgs = []; - fakeDb.save = function (key: string, value: any): void { - saveSpyCallCount++; - saveSpyArgs.push([key, value]); - return originalSave(key, value); - }; }); afterEach(() => { - // Restore original method if necessary - fakeDb.save = originalSave; + // Restore all mocks created by node:test’s mock helper. + mock.restoreAll(); }); it('should save and return the correct value', () => { - // Call the function under test + // Create a spy on the save method using node:test's mock helper. + const saveSpy = mock.method(fakeDb, 'save').mock; + + // Call the function under test. const result = someTestFunction(fakeDb, testKey, testValue); - // Verify state + // Verify state. assert.deepStrictEqual(result, testValue); assert.strictEqual(result.first, 'John'); assert.strictEqual(result.last, 'Jones'); assert.strictEqual(result.lastUpdated, testValue.lastUpdated); - // Verify behavior using our manual spy - assert.strictEqual(saveSpyCallCount, 1); - assert.deepStrictEqual(saveSpyArgs[0], [testKey, testValue]); + // Verify behavior using the spy. + assert.strictEqual(saveSpy.callCount(), 1); + const calls = saveSpy.calls; + assert.deepStrictEqual(calls[0].arguments, [testKey, testValue]); }); }); \ No newline at end of file diff --git a/testrunner-typescript/src/test-app/insert.test.ts b/test-with-node-testrunner/test/insert.test.ts similarity index 89% rename from testrunner-typescript/src/test-app/insert.test.ts rename to test-with-node-testrunner/test/insert.test.ts index ce3615d7..a813790a 100644 --- a/testrunner-typescript/src/test-app/insert.test.ts +++ b/test-with-node-testrunner/test/insert.test.ts @@ -1,24 +1,24 @@ // insertDocument.test.ts import { describe, it, beforeEach, mock } from 'node:test'; import assert from 'node:assert'; -import { Container } from '../app/data/connect-to-cosmos'; -import { createTestInputAndResult } from '../app/data/fake-data'; +import { Container } from '../src/data/connect-to-cosmos'; +import { createTestInputAndResult } from '../src/data/fake-data'; import type { DbDocument, DbError, - RawInput -} from '../app/data/model'; + RawInput, +} from '../src/data/model'; import { isDbError, - isVerificationErrors -} from '../app/data/model'; + isVerificationErrors, +} from '../src/data/model'; // Instead of jest.mock, import the whole module to override functions as needed. -import Verify from '../app/data/verify'; -import CosmosConnector from '../app/data/connect-to-cosmos'; -import { insertDocument } from '../app/lib/insert'; +import Verify from '../src/data/verify'; +import CosmosConnector from '../src/data/connect-to-cosmos'; +import { insertDocument } from '../src/lib/insert'; // --- Test suite for insertDocument --- -describe('Insert into db', () => { +describe('SDK', () => { // Set up a fresh container object before each test. beforeEach(() => { @@ -59,8 +59,6 @@ describe('Insert into db', () => { name: result.name, }); }); - - it('should return verification error if input is not verified', async () => { const fakeContainer = { @@ -96,8 +94,6 @@ describe('Insert into db', () => { assert.strictEqual(mContainerCreate.callCount(), 0); }); - - it('should return error if db insert fails', async () => { // Arrange: override inputVerified to return true. const { input, result } = createTestInputAndResult(); @@ -115,13 +111,13 @@ describe('Insert into db', () => { mVerify.mockImplementation(() => true); const mContainerCreate = mock.method(fakeContainer.items, "create").mock; - mContainerCreate.mockImplementation = async (doc: any) => { + mContainerCreate.mockImplementation(async (doc: any) => { const mockError: DbError = { message: errorMessage, code: 500, }; throw mockError; - } + }); // Act: const insertDocumentResult = await insertDocument(fakeContainer, input); @@ -129,10 +125,9 @@ describe('Insert into db', () => { // // Assert - Ensure create method was called once with the correct arguments. assert.strictEqual(isDbError(insertDocumentResult), true); assert.strictEqual(mContainerCreate.callCount(), 1); - assert.deepStrictEqual(mContainerCreate.calls[0].arguments[0], { id: input.id, name: result.name, }); }); -}); +}); \ No newline at end of file diff --git a/test-with-node-testrunner/tsconfig.json b/test-with-node-testrunner/tsconfig.json new file mode 100644 index 00000000..e8be0f7f --- /dev/null +++ b/test-with-node-testrunner/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "es6", // Specify ECMAScript target version + "module": "commonjs", // Specify module code generation + "strict": true, // Enable all strict type-checking options + "esModuleInterop": true, // Enables emit interoperability between CommonJS and ES Modules + "noImplicitAny": false, // Enable error reporting for expressions and declarations with an implied 'any' type + "forceConsistentCasingInFileNames": true, // Disallow inconsistently-cased references to the same file + "outDir": "./dist", + "rootDir": "./", + }, + "include": [ + "src", + "test" + ], + "exclude": [ + "node_modules" + ] +} \ No newline at end of file diff --git a/test-with-vitest/README.md b/test-with-vitest/README.md new file mode 100644 index 00000000..55e5d1bc --- /dev/null +++ b/test-with-vitest/README.md @@ -0,0 +1,52 @@ +# Vitest for the Azure SDK for JavaScript + +This subfolder is the source code for the [How to Test Azure SDK Integration in JavaScript Applications](https://learn.microsoft.com/azure/developer/javascript/sdk/test-sdk-integration). The purpose is to demonstrate unit test mocks for the Azure SDK for JavaScript. These specific examples use Azure Cosmos DB. + +## To run the test + +1. `npm install` +2. `npm run build` +3. `npm test` + + ```console + > test-with-vitest@1.0.0 test + > vitest run --coverage + + + RUN v3.0.8 /workspaces/node-essentials/test-with-vitest + Coverage enabled with istanbul + + ✓ tests/02-stubs.test.ts (2 tests) 6ms + ✓ tests/boilerplate.test.ts (1 test) 6ms + ✓ tests/boilerplate-with-mock.test.ts (1 test) 5ms + ✓ tests/fake-in-mem-db.test.ts (1 test) 10ms + ✓ tests/01-spies.test.ts (1 test) 7ms + ✓ tests/insert.test.ts (3 tests) 11ms + + Test Files 6 passed (6) + Tests 9 passed (9) + Start at 18:36:27 + Duration 14.01s (transform 5.89s, setup 0ms, collect 12.28s, tests 45ms, environment 2ms, prepare 12.26s) + + % Coverage report from istanbul + -----------------------|---------|----------|---------|---------|------------------- + File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s + -----------------------|---------|----------|---------|---------|------------------- + All files | 36.95 | 31.25 | 42.85 | 36.95 | + src | 0 | 100 | 0 | 0 | + index.ts | 0 | 100 | 0 | 0 | 7-16 + src/data | 32.14 | 20 | 50 | 32.14 | + connect-to-cosmos.ts | 0 | 100 | 0 | 0 | 12-37 + fake-data.ts | 100 | 100 | 100 | 100 | + model.ts | 25 | 20 | 66.66 | 25 | 26-38 + verify.ts | 0 | 100 | 0 | 0 | 6-7 + src/lib | 72.72 | 50 | 100 | 72.72 | + insert.ts | 72.72 | 50 | 100 | 72.72 | 30-36 + -----------------------|---------|----------|---------|---------|------------------- + ``` + +## Related content + +* [Passwordless connections for Azure services](https://learn.microsoft.com/azure/developer/intro/passwordless-overview) +* [Cosmos DB keyless access to the service](https://learn.microsoft.com/azure/cosmos-db/role-based-access-control) +* [Vitest](https://main.vitest.dev/) \ No newline at end of file diff --git a/test-with-vitest/coverage/lcov-report/base.css b/test-with-vitest/coverage/lcov-report/base.css new file mode 100644 index 00000000..f418035b --- /dev/null +++ b/test-with-vitest/coverage/lcov-report/base.css @@ -0,0 +1,224 @@ +body, html { + margin:0; padding: 0; + height: 100%; +} +body { + font-family: Helvetica Neue, Helvetica, Arial; + font-size: 14px; + color:#333; +} +.small { font-size: 12px; } +*, *:after, *:before { + -webkit-box-sizing:border-box; + -moz-box-sizing:border-box; + box-sizing:border-box; + } +h1 { font-size: 20px; margin: 0;} +h2 { font-size: 14px; } +pre { + font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace; + margin: 0; + padding: 0; + -moz-tab-size: 2; + -o-tab-size: 2; + tab-size: 2; +} +a { color:#0074D9; text-decoration:none; } +a:hover { text-decoration:underline; } +.strong { font-weight: bold; } +.space-top1 { padding: 10px 0 0 0; } +.pad2y { padding: 20px 0; } +.pad1y { padding: 10px 0; } +.pad2x { padding: 0 20px; } +.pad2 { padding: 20px; } +.pad1 { padding: 10px; } +.space-left2 { padding-left:55px; } +.space-right2 { padding-right:20px; } +.center { text-align:center; } +.clearfix { display:block; } +.clearfix:after { + content:''; + display:block; + height:0; + clear:both; + visibility:hidden; + } +.fl { float: left; } +@media only screen and (max-width:640px) { + .col3 { width:100%; max-width:100%; } + .hide-mobile { display:none!important; } +} + +.quiet { + color: #7f7f7f; + color: rgba(0,0,0,0.5); +} +.quiet a { opacity: 0.7; } + +.fraction { + font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; + font-size: 10px; + color: #555; + background: #E8E8E8; + padding: 4px 5px; + border-radius: 3px; + vertical-align: middle; +} + +div.path a:link, div.path a:visited { color: #333; } +table.coverage { + border-collapse: collapse; + margin: 10px 0 0 0; + padding: 0; +} + +table.coverage td { + margin: 0; + padding: 0; + vertical-align: top; +} +table.coverage td.line-count { + text-align: right; + padding: 0 5px 0 20px; +} +table.coverage td.line-coverage { + text-align: right; + padding-right: 10px; + min-width:20px; +} + +table.coverage td span.cline-any { + display: inline-block; + padding: 0 5px; + width: 100%; +} +.missing-if-branch { + display: inline-block; + margin-right: 5px; + border-radius: 3px; + position: relative; + padding: 0 4px; + background: #333; + color: yellow; +} + +.skip-if-branch { + display: none; + margin-right: 10px; + position: relative; + padding: 0 4px; + background: #ccc; + color: white; +} +.missing-if-branch .typ, .skip-if-branch .typ { + color: inherit !important; +} +.coverage-summary { + border-collapse: collapse; + width: 100%; +} +.coverage-summary tr { border-bottom: 1px solid #bbb; } +.keyline-all { border: 1px solid #ddd; } +.coverage-summary td, .coverage-summary th { padding: 10px; } +.coverage-summary tbody { border: 1px solid #bbb; } +.coverage-summary td { border-right: 1px solid #bbb; } +.coverage-summary td:last-child { border-right: none; } +.coverage-summary th { + text-align: left; + font-weight: normal; + white-space: nowrap; +} +.coverage-summary th.file { border-right: none !important; } +.coverage-summary th.pct { } +.coverage-summary th.pic, +.coverage-summary th.abs, +.coverage-summary td.pct, +.coverage-summary td.abs { text-align: right; } +.coverage-summary td.file { white-space: nowrap; } +.coverage-summary td.pic { min-width: 120px !important; } +.coverage-summary tfoot td { } + +.coverage-summary .sorter { + height: 10px; + width: 7px; + display: inline-block; + margin-left: 0.5em; + background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent; +} +.coverage-summary .sorted .sorter { + background-position: 0 -20px; +} +.coverage-summary .sorted-desc .sorter { + background-position: 0 -10px; +} +.status-line { height: 10px; } +/* yellow */ +.cbranch-no { background: yellow !important; color: #111; } +/* dark red */ +.red.solid, .status-line.low, .low .cover-fill { background:#C21F39 } +.low .chart { border:1px solid #C21F39 } +.highlighted, +.highlighted .cstat-no, .highlighted .fstat-no, .highlighted .cbranch-no{ + background: #C21F39 !important; +} +/* medium red */ +.cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE } +/* light red */ +.low, .cline-no { background:#FCE1E5 } +/* light green */ +.high, .cline-yes { background:rgb(230,245,208) } +/* medium green */ +.cstat-yes { background:rgb(161,215,106) } +/* dark green */ +.status-line.high, .high .cover-fill { background:rgb(77,146,33) } +.high .chart { border:1px solid rgb(77,146,33) } +/* dark yellow (gold) */ +.status-line.medium, .medium .cover-fill { background: #f9cd0b; } +.medium .chart { border:1px solid #f9cd0b; } +/* light yellow */ +.medium { background: #fff4c2; } + +.cstat-skip { background: #ddd; color: #111; } +.fstat-skip { background: #ddd; color: #111 !important; } +.cbranch-skip { background: #ddd !important; color: #111; } + +span.cline-neutral { background: #eaeaea; } + +.coverage-summary td.empty { + opacity: .5; + padding-top: 4px; + padding-bottom: 4px; + line-height: 1; + color: #888; +} + +.cover-fill, .cover-empty { + display:inline-block; + height: 12px; +} +.chart { + line-height: 0; +} +.cover-empty { + background: white; +} +.cover-full { + border-right: none !important; +} +pre.prettyprint { + border: none !important; + padding: 0 !important; + margin: 0 !important; +} +.com { color: #999 !important; } +.ignore-none { color: #999; font-weight: normal; } + +.wrapper { + min-height: 100%; + height: auto !important; + height: 100%; + margin: 0 auto -48px; +} +.footer, .push { + height: 48px; +} diff --git a/test-with-vitest/coverage/lcov-report/block-navigation.js b/test-with-vitest/coverage/lcov-report/block-navigation.js new file mode 100644 index 00000000..cc121302 --- /dev/null +++ b/test-with-vitest/coverage/lcov-report/block-navigation.js @@ -0,0 +1,87 @@ +/* eslint-disable */ +var jumpToCode = (function init() { + // Classes of code we would like to highlight in the file view + var missingCoverageClasses = ['.cbranch-no', '.cstat-no', '.fstat-no']; + + // Elements to highlight in the file listing view + var fileListingElements = ['td.pct.low']; + + // We don't want to select elements that are direct descendants of another match + var notSelector = ':not(' + missingCoverageClasses.join('):not(') + ') > '; // becomes `:not(a):not(b) > ` + + // Selecter that finds elements on the page to which we can jump + var selector = + fileListingElements.join(', ') + + ', ' + + notSelector + + missingCoverageClasses.join(', ' + notSelector); // becomes `:not(a):not(b) > a, :not(a):not(b) > b` + + // The NodeList of matching elements + var missingCoverageElements = document.querySelectorAll(selector); + + var currentIndex; + + function toggleClass(index) { + missingCoverageElements + .item(currentIndex) + .classList.remove('highlighted'); + missingCoverageElements.item(index).classList.add('highlighted'); + } + + function makeCurrent(index) { + toggleClass(index); + currentIndex = index; + missingCoverageElements.item(index).scrollIntoView({ + behavior: 'smooth', + block: 'center', + inline: 'center' + }); + } + + function goToPrevious() { + var nextIndex = 0; + if (typeof currentIndex !== 'number' || currentIndex === 0) { + nextIndex = missingCoverageElements.length - 1; + } else if (missingCoverageElements.length > 1) { + nextIndex = currentIndex - 1; + } + + makeCurrent(nextIndex); + } + + function goToNext() { + var nextIndex = 0; + + if ( + typeof currentIndex === 'number' && + currentIndex < missingCoverageElements.length - 1 + ) { + nextIndex = currentIndex + 1; + } + + makeCurrent(nextIndex); + } + + return function jump(event) { + if ( + document.getElementById('fileSearch') === document.activeElement && + document.activeElement != null + ) { + // if we're currently focused on the search input, we don't want to navigate + return; + } + + switch (event.which) { + case 78: // n + case 74: // j + goToNext(); + break; + case 66: // b + case 75: // k + case 80: // p + goToPrevious(); + break; + } + }; +})(); +window.addEventListener('keydown', jumpToCode); diff --git a/test-with-vitest/coverage/lcov-report/favicon.png b/test-with-vitest/coverage/lcov-report/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..c1525b811a167671e9de1fa78aab9f5c0b61cef7 GIT binary patch literal 445 zcmV;u0Yd(XP))rP{nL}Ln%S7`m{0DjX9TLF* zFCb$4Oi7vyLOydb!7n&^ItCzb-%BoB`=x@N2jll2Nj`kauio%aw_@fe&*}LqlFT43 z8doAAe))z_%=P%v^@JHp3Hjhj^6*Kr_h|g_Gr?ZAa&y>wxHE99Gk>A)2MplWz2xdG zy8VD2J|Uf#EAw*bo5O*PO_}X2Tob{%bUoO2G~T`@%S6qPyc}VkhV}UifBuRk>%5v( z)x7B{I~z*k<7dv#5tC+m{km(D087J4O%+<<;K|qwefb6@GSX45wCK}Sn*> + + + + Code coverage report for All files + + + + + + + + + +
+
+

All files

+
+ +
+ 36.95% + Statements + 17/46 +
+ + +
+ 31.25% + Branches + 5/16 +
+ + +
+ 42.85% + Functions + 6/14 +
+ + +
+ 36.95% + Lines + 17/46 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
src +
+
0%0/7100%0/00%0/30%0/7
src/data +
+
32.14%9/2820%2/1050%5/1032.14%9/28
src/lib +
+
72.72%8/1150%3/6100%1/172.72%8/11
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/test-with-vitest/coverage/lcov-report/prettify.css b/test-with-vitest/coverage/lcov-report/prettify.css new file mode 100644 index 00000000..b317a7cd --- /dev/null +++ b/test-with-vitest/coverage/lcov-report/prettify.css @@ -0,0 +1 @@ +.pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee} diff --git a/test-with-vitest/coverage/lcov-report/prettify.js b/test-with-vitest/coverage/lcov-report/prettify.js new file mode 100644 index 00000000..b3225238 --- /dev/null +++ b/test-with-vitest/coverage/lcov-report/prettify.js @@ -0,0 +1,2 @@ +/* eslint-disable */ +window.PR_SHOULD_USE_CONTINUATION=true;(function(){var h=["break,continue,do,else,for,if,return,while"];var u=[h,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"];var p=[u,"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"];var l=[p,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"];var x=[p,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"];var R=[x,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"];var r="all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes";var w=[p,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"];var s="caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END";var I=[h,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"];var f=[h,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"];var H=[h,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"];var A=[l,R,w,s+I,f,H];var e=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/;var C="str";var z="kwd";var j="com";var O="typ";var G="lit";var L="pun";var F="pln";var m="tag";var E="dec";var J="src";var P="atn";var n="atv";var N="nocode";var M="(?:^^\\.?|[+-]|\\!|\\!=|\\!==|\\#|\\%|\\%=|&|&&|&&=|&=|\\(|\\*|\\*=|\\+=|\\,|\\-=|\\->|\\/|\\/=|:|::|\\;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\@|\\[|\\^|\\^=|\\^\\^|\\^\\^=|\\{|\\||\\|=|\\|\\||\\|\\|=|\\~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*";function k(Z){var ad=0;var S=false;var ac=false;for(var V=0,U=Z.length;V122)){if(!(al<65||ag>90)){af.push([Math.max(65,ag)|32,Math.min(al,90)|32])}if(!(al<97||ag>122)){af.push([Math.max(97,ag)&~32,Math.min(al,122)&~32])}}}}af.sort(function(av,au){return(av[0]-au[0])||(au[1]-av[1])});var ai=[];var ap=[NaN,NaN];for(var ar=0;arat[0]){if(at[1]+1>at[0]){an.push("-")}an.push(T(at[1]))}}an.push("]");return an.join("")}function W(al){var aj=al.source.match(new RegExp("(?:\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]|\\\\u[A-Fa-f0-9]{4}|\\\\x[A-Fa-f0-9]{2}|\\\\[0-9]+|\\\\[^ux0-9]|\\(\\?[:!=]|[\\(\\)\\^]|[^\\x5B\\x5C\\(\\)\\^]+)","g"));var ah=aj.length;var an=[];for(var ak=0,am=0;ak=2&&ai==="["){aj[ak]=X(ag)}else{if(ai!=="\\"){aj[ak]=ag.replace(/[a-zA-Z]/g,function(ao){var ap=ao.charCodeAt(0);return"["+String.fromCharCode(ap&~32,ap|32)+"]"})}}}}return aj.join("")}var aa=[];for(var V=0,U=Z.length;V=0;){S[ac.charAt(ae)]=Y}}var af=Y[1];var aa=""+af;if(!ag.hasOwnProperty(aa)){ah.push(af);ag[aa]=null}}ah.push(/[\0-\uffff]/);V=k(ah)})();var X=T.length;var W=function(ah){var Z=ah.sourceCode,Y=ah.basePos;var ad=[Y,F];var af=0;var an=Z.match(V)||[];var aj={};for(var ae=0,aq=an.length;ae=5&&"lang-"===ap.substring(0,5);if(am&&!(ai&&typeof ai[1]==="string")){am=false;ap=J}if(!am){aj[ag]=ap}}var ab=af;af+=ag.length;if(!am){ad.push(Y+ab,ap)}else{var al=ai[1];var ak=ag.indexOf(al);var ac=ak+al.length;if(ai[2]){ac=ag.length-ai[2].length;ak=ac-al.length}var ar=ap.substring(5);B(Y+ab,ag.substring(0,ak),W,ad);B(Y+ab+ak,al,q(ar,al),ad);B(Y+ab+ac,ag.substring(ac),W,ad)}}ah.decorations=ad};return W}function i(T){var W=[],S=[];if(T.tripleQuotedStrings){W.push([C,/^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,null,"'\""])}else{if(T.multiLineStrings){W.push([C,/^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,null,"'\"`"])}else{W.push([C,/^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,null,"\"'"])}}if(T.verbatimStrings){S.push([C,/^@\"(?:[^\"]|\"\")*(?:\"|$)/,null])}var Y=T.hashComments;if(Y){if(T.cStyleComments){if(Y>1){W.push([j,/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,null,"#"])}else{W.push([j,/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\r\n]*)/,null,"#"])}S.push([C,/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,null])}else{W.push([j,/^#[^\r\n]*/,null,"#"])}}if(T.cStyleComments){S.push([j,/^\/\/[^\r\n]*/,null]);S.push([j,/^\/\*[\s\S]*?(?:\*\/|$)/,null])}if(T.regexLiterals){var X=("/(?=[^/*])(?:[^/\\x5B\\x5C]|\\x5C[\\s\\S]|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+/");S.push(["lang-regex",new RegExp("^"+M+"("+X+")")])}var V=T.types;if(V){S.push([O,V])}var U=(""+T.keywords).replace(/^ | $/g,"");if(U.length){S.push([z,new RegExp("^(?:"+U.replace(/[\s,]+/g,"|")+")\\b"),null])}W.push([F,/^\s+/,null," \r\n\t\xA0"]);S.push([G,/^@[a-z_$][a-z_$@0-9]*/i,null],[O,/^(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\w+_t\b)/,null],[F,/^[a-z_$][a-z_$@0-9]*/i,null],[G,new RegExp("^(?:0x[a-f0-9]+|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)(?:e[+\\-]?\\d+)?)[a-z]*","i"),null,"0123456789"],[F,/^\\[\s\S]?/,null],[L,/^.[^\s\w\.$@\'\"\`\/\#\\]*/,null]);return g(W,S)}var K=i({keywords:A,hashComments:true,cStyleComments:true,multiLineStrings:true,regexLiterals:true});function Q(V,ag){var U=/(?:^|\s)nocode(?:\s|$)/;var ab=/\r\n?|\n/;var ac=V.ownerDocument;var S;if(V.currentStyle){S=V.currentStyle.whiteSpace}else{if(window.getComputedStyle){S=ac.defaultView.getComputedStyle(V,null).getPropertyValue("white-space")}}var Z=S&&"pre"===S.substring(0,3);var af=ac.createElement("LI");while(V.firstChild){af.appendChild(V.firstChild)}var W=[af];function ae(al){switch(al.nodeType){case 1:if(U.test(al.className)){break}if("BR"===al.nodeName){ad(al);if(al.parentNode){al.parentNode.removeChild(al)}}else{for(var an=al.firstChild;an;an=an.nextSibling){ae(an)}}break;case 3:case 4:if(Z){var am=al.nodeValue;var aj=am.match(ab);if(aj){var ai=am.substring(0,aj.index);al.nodeValue=ai;var ah=am.substring(aj.index+aj[0].length);if(ah){var ak=al.parentNode;ak.insertBefore(ac.createTextNode(ah),al.nextSibling)}ad(al);if(!ai){al.parentNode.removeChild(al)}}}break}}function ad(ak){while(!ak.nextSibling){ak=ak.parentNode;if(!ak){return}}function ai(al,ar){var aq=ar?al.cloneNode(false):al;var ao=al.parentNode;if(ao){var ap=ai(ao,1);var an=al.nextSibling;ap.appendChild(aq);for(var am=an;am;am=an){an=am.nextSibling;ap.appendChild(am)}}return aq}var ah=ai(ak.nextSibling,0);for(var aj;(aj=ah.parentNode)&&aj.nodeType===1;){ah=aj}W.push(ah)}for(var Y=0;Y=S){ah+=2}if(V>=ap){Z+=2}}}var t={};function c(U,V){for(var S=V.length;--S>=0;){var T=V[S];if(!t.hasOwnProperty(T)){t[T]=U}else{if(window.console){console.warn("cannot override language handler %s",T)}}}}function q(T,S){if(!(T&&t.hasOwnProperty(T))){T=/^\s*]*(?:>|$)/],[j,/^<\!--[\s\S]*?(?:-\->|$)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],[L,/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),["default-markup","htm","html","mxml","xhtml","xml","xsl"]);c(g([[F,/^[\s]+/,null," \t\r\n"],[n,/^(?:\"[^\"]*\"?|\'[^\']*\'?)/,null,"\"'"]],[[m,/^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],[P,/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],[L,/^[=<>\/]+/],["lang-js",/^on\w+\s*=\s*\"([^\"]+)\"/i],["lang-js",/^on\w+\s*=\s*\'([^\']+)\'/i],["lang-js",/^on\w+\s*=\s*([^\"\'>\s]+)/i],["lang-css",/^style\s*=\s*\"([^\"]+)\"/i],["lang-css",/^style\s*=\s*\'([^\']+)\'/i],["lang-css",/^style\s*=\s*([^\"\'>\s]+)/i]]),["in.tag"]);c(g([],[[n,/^[\s\S]+/]]),["uq.val"]);c(i({keywords:l,hashComments:true,cStyleComments:true,types:e}),["c","cc","cpp","cxx","cyc","m"]);c(i({keywords:"null,true,false"}),["json"]);c(i({keywords:R,hashComments:true,cStyleComments:true,verbatimStrings:true,types:e}),["cs"]);c(i({keywords:x,cStyleComments:true}),["java"]);c(i({keywords:H,hashComments:true,multiLineStrings:true}),["bsh","csh","sh"]);c(i({keywords:I,hashComments:true,multiLineStrings:true,tripleQuotedStrings:true}),["cv","py"]);c(i({keywords:s,hashComments:true,multiLineStrings:true,regexLiterals:true}),["perl","pl","pm"]);c(i({keywords:f,hashComments:true,multiLineStrings:true,regexLiterals:true}),["rb"]);c(i({keywords:w,cStyleComments:true,regexLiterals:true}),["js"]);c(i({keywords:r,hashComments:3,cStyleComments:true,multilineStrings:true,tripleQuotedStrings:true,regexLiterals:true}),["coffee"]);c(g([],[[C,/^[\s\S]+/]]),["regex"]);function d(V){var U=V.langExtension;try{var S=a(V.sourceNode);var T=S.sourceCode;V.sourceCode=T;V.spans=S.spans;V.basePos=0;q(U,T)(V);D(V)}catch(W){if("console" in window){console.log(W&&W.stack?W.stack:W)}}}function y(W,V,U){var S=document.createElement("PRE");S.innerHTML=W;if(U){Q(S,U)}var T={langExtension:V,numberLines:U,sourceNode:S};d(T);return S.innerHTML}function b(ad){function Y(af){return document.getElementsByTagName(af)}var ac=[Y("pre"),Y("code"),Y("xmp")];var T=[];for(var aa=0;aa=0){var ah=ai.match(ab);var am;if(!ah&&(am=o(aj))&&"CODE"===am.tagName){ah=am.className.match(ab)}if(ah){ah=ah[1]}var al=false;for(var ak=aj.parentNode;ak;ak=ak.parentNode){if((ak.tagName==="pre"||ak.tagName==="code"||ak.tagName==="xmp")&&ak.className&&ak.className.indexOf("prettyprint")>=0){al=true;break}}if(!al){var af=aj.className.match(/\blinenums\b(?::(\d+))?/);af=af?af[1]&&af[1].length?+af[1]:true:false;if(af){Q(aj,af)}S={langExtension:ah,sourceNode:aj,numberLines:af};d(S)}}}if(X]*(?:>|$)/],[PR.PR_COMMENT,/^<\!--[\s\S]*?(?:-\->|$)/],[PR.PR_PUNCTUATION,/^(?:<[%?]|[%?]>)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-handlebars",/^]*type\s*=\s*['"]?text\/x-handlebars-template['"]?\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i],[PR.PR_DECLARATION,/^{{[#^>/]?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{&?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{{>?\s*[\w.][^}]*}}}/],[PR.PR_COMMENT,/^{{![^}]*}}/]]),["handlebars","hbs"]);PR.registerLangHandler(PR.createSimpleLexer([[PR.PR_PLAIN,/^[ \t\r\n\f]+/,null," \t\r\n\f"]],[[PR.PR_STRING,/^\"(?:[^\n\r\f\\\"]|\\(?:\r\n?|\n|\f)|\\[\s\S])*\"/,null],[PR.PR_STRING,/^\'(?:[^\n\r\f\\\']|\\(?:\r\n?|\n|\f)|\\[\s\S])*\'/,null],["lang-css-str",/^url\(([^\)\"\']*)\)/i],[PR.PR_KEYWORD,/^(?:url|rgb|\!important|@import|@page|@media|@charset|inherit)(?=[^\-\w]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|(?:\\[0-9a-f]+ ?))(?:[_a-z0-9\-]|\\(?:\\[0-9a-f]+ ?))*)\s*:/i],[PR.PR_COMMENT,/^\/\*[^*]*\*+(?:[^\/*][^*]*\*+)*\//],[PR.PR_COMMENT,/^(?:)/],[PR.PR_LITERAL,/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],[PR.PR_LITERAL,/^#(?:[0-9a-f]{3}){1,2}/i],[PR.PR_PLAIN,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i],[PR.PR_PUNCTUATION,/^[^\s\w\'\"]+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_KEYWORD,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_STRING,/^[^\)\"\']+/]]),["css-str"]); diff --git a/test-with-vitest/coverage/lcov-report/sort-arrow-sprite.png b/test-with-vitest/coverage/lcov-report/sort-arrow-sprite.png new file mode 100644 index 0000000000000000000000000000000000000000..6ed68316eb3f65dec9063332d2f69bf3093bbfab GIT binary patch literal 138 zcmeAS@N?(olHy`uVBq!ia0vp^>_9Bd!3HEZxJ@+%Qh}Z>jv*C{$p!i!8j}?a+@3A= zIAGwzjijN=FBi!|L1t?LM;Q;gkwn>2cAy-KV{dn nf0J1DIvEHQu*n~6U}x}qyky7vi4|9XhBJ7&`njxgN@xNA8m%nc literal 0 HcmV?d00001 diff --git a/test-with-vitest/coverage/lcov-report/sorter.js b/test-with-vitest/coverage/lcov-report/sorter.js new file mode 100644 index 00000000..2bb296a8 --- /dev/null +++ b/test-with-vitest/coverage/lcov-report/sorter.js @@ -0,0 +1,196 @@ +/* eslint-disable */ +var addSorting = (function() { + 'use strict'; + var cols, + currentSort = { + index: 0, + desc: false + }; + + // returns the summary table element + function getTable() { + return document.querySelector('.coverage-summary'); + } + // returns the thead element of the summary table + function getTableHeader() { + return getTable().querySelector('thead tr'); + } + // returns the tbody element of the summary table + function getTableBody() { + return getTable().querySelector('tbody'); + } + // returns the th element for nth column + function getNthColumn(n) { + return getTableHeader().querySelectorAll('th')[n]; + } + + function onFilterInput() { + const searchValue = document.getElementById('fileSearch').value; + const rows = document.getElementsByTagName('tbody')[0].children; + for (let i = 0; i < rows.length; i++) { + const row = rows[i]; + if ( + row.textContent + .toLowerCase() + .includes(searchValue.toLowerCase()) + ) { + row.style.display = ''; + } else { + row.style.display = 'none'; + } + } + } + + // loads the search box + function addSearchBox() { + var template = document.getElementById('filterTemplate'); + var templateClone = template.content.cloneNode(true); + templateClone.getElementById('fileSearch').oninput = onFilterInput; + template.parentElement.appendChild(templateClone); + } + + // loads all columns + function loadColumns() { + var colNodes = getTableHeader().querySelectorAll('th'), + colNode, + cols = [], + col, + i; + + for (i = 0; i < colNodes.length; i += 1) { + colNode = colNodes[i]; + col = { + key: colNode.getAttribute('data-col'), + sortable: !colNode.getAttribute('data-nosort'), + type: colNode.getAttribute('data-type') || 'string' + }; + cols.push(col); + if (col.sortable) { + col.defaultDescSort = col.type === 'number'; + colNode.innerHTML = + colNode.innerHTML + ''; + } + } + return cols; + } + // attaches a data attribute to every tr element with an object + // of data values keyed by column name + function loadRowData(tableRow) { + var tableCols = tableRow.querySelectorAll('td'), + colNode, + col, + data = {}, + i, + val; + for (i = 0; i < tableCols.length; i += 1) { + colNode = tableCols[i]; + col = cols[i]; + val = colNode.getAttribute('data-value'); + if (col.type === 'number') { + val = Number(val); + } + data[col.key] = val; + } + return data; + } + // loads all row data + function loadData() { + var rows = getTableBody().querySelectorAll('tr'), + i; + + for (i = 0; i < rows.length; i += 1) { + rows[i].data = loadRowData(rows[i]); + } + } + // sorts the table using the data for the ith column + function sortByIndex(index, desc) { + var key = cols[index].key, + sorter = function(a, b) { + a = a.data[key]; + b = b.data[key]; + return a < b ? -1 : a > b ? 1 : 0; + }, + finalSorter = sorter, + tableBody = document.querySelector('.coverage-summary tbody'), + rowNodes = tableBody.querySelectorAll('tr'), + rows = [], + i; + + if (desc) { + finalSorter = function(a, b) { + return -1 * sorter(a, b); + }; + } + + for (i = 0; i < rowNodes.length; i += 1) { + rows.push(rowNodes[i]); + tableBody.removeChild(rowNodes[i]); + } + + rows.sort(finalSorter); + + for (i = 0; i < rows.length; i += 1) { + tableBody.appendChild(rows[i]); + } + } + // removes sort indicators for current column being sorted + function removeSortIndicators() { + var col = getNthColumn(currentSort.index), + cls = col.className; + + cls = cls.replace(/ sorted$/, '').replace(/ sorted-desc$/, ''); + col.className = cls; + } + // adds sort indicators for current column being sorted + function addSortIndicators() { + getNthColumn(currentSort.index).className += currentSort.desc + ? ' sorted-desc' + : ' sorted'; + } + // adds event listeners for all sorter widgets + function enableUI() { + var i, + el, + ithSorter = function ithSorter(i) { + var col = cols[i]; + + return function() { + var desc = col.defaultDescSort; + + if (currentSort.index === i) { + desc = !currentSort.desc; + } + sortByIndex(i, desc); + removeSortIndicators(); + currentSort.index = i; + currentSort.desc = desc; + addSortIndicators(); + }; + }; + for (i = 0; i < cols.length; i += 1) { + if (cols[i].sortable) { + // add the click event handler on the th so users + // dont have to click on those tiny arrows + el = getNthColumn(i).querySelector('.sorter').parentElement; + if (el.addEventListener) { + el.addEventListener('click', ithSorter(i)); + } else { + el.attachEvent('onclick', ithSorter(i)); + } + } + } + } + // adds sorting functionality to the UI + return function() { + if (!getTable()) { + return; + } + cols = loadColumns(); + loadData(); + addSearchBox(); + addSortIndicators(); + enableUI(); + }; +})(); + +window.addEventListener('load', addSorting); diff --git a/test-with-vitest/coverage/lcov-report/src/data/connect-to-cosmos.ts.html b/test-with-vitest/coverage/lcov-report/src/data/connect-to-cosmos.ts.html new file mode 100644 index 00000000..8d316e4d --- /dev/null +++ b/test-with-vitest/coverage/lcov-report/src/data/connect-to-cosmos.ts.html @@ -0,0 +1,202 @@ + + + + + + Code coverage report for src/data/connect-to-cosmos.ts + + + + + + + + + +
+
+

All files / src/data connect-to-cosmos.ts

+
+ +
+ 0% + Statements + 0/11 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/3 +
+ + +
+ 0% + Lines + 0/11 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
// connect-to-cosmos.ts
+ 
+import { Container, CosmosClient } from '@azure/cosmos';
+import { DefaultAzureCredential } from '@azure/identity';
+import 'dotenv/config';
+import { v4 as uuidv4 } from 'uuid';
+ 
+export { Container };
+ 
+export default class CosmosConnector {
+  static connectToCosmosWithoutKey(): CosmosClient {
+    const endpoint = process.env.COSMOS_DB_ENDPOINT!;
+    const credential = new DefaultAzureCredential();
+    const client = new CosmosClient({ endpoint, aadCredentials: credential });
+    return client;
+  }
+ 
+  static async connectToContainer(): Promise<Container> {
+    const client = CosmosConnector.connectToCosmosWithoutKey();
+    const databaseName = process.env.COSMOS_DATABASE_NAME!;
+    const containerName = process.env.COSMOS_CONTAINER_NAME!;
+ 
+    // Ensure the database exists
+    const { database } = await client.databases.createIfNotExists({
+      id: databaseName,
+    });
+ 
+    // Ensure the container exists
+    const { container } = await database.containers.createIfNotExists({
+      id: containerName,
+    });
+ 
+    return container;
+  }
+ 
+  static getUniqueId(): string {
+    return uuidv4();
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/test-with-vitest/coverage/lcov-report/src/data/fake-data.ts.html b/test-with-vitest/coverage/lcov-report/src/data/fake-data.ts.html new file mode 100644 index 00000000..bce1d06e --- /dev/null +++ b/test-with-vitest/coverage/lcov-report/src/data/fake-data.ts.html @@ -0,0 +1,169 @@ + + + + + + Code coverage report for src/data/fake-data.ts + + + + + + + + + +
+
+

All files / src/data fake-data.ts

+
+ +
+ 100% + Statements + 7/7 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 3/3 +
+ + +
+ 100% + Lines + 7/7 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29  +  +  +  +  +2x +  +  +  +2x +  +  +  +2x +2x +  +  +  +  +  +  +2x +2x +  +  +  +2x +  + 
import { v4 as uuidv4 } from 'uuid';
+import { faker } from '@faker-js/faker';
+import type { DbDocument, RawInput } from './model.js';
+ 
+function createFixture<T>(): T {
+  const result = {
+    first: faker.person.firstName(),
+    last: faker.person.lastName(),
+  };
+  return result as T;
+}
+ 
+export function createTestInput(): RawInput {
+  const { first, last } = createFixture<RawInput>();
+  return { id: uuidv4(), first, last };
+}
+ 
+export function createTestInputAndResult(): {
+  input: RawInput;
+  result: Partial<DbDocument>;
+} {
+  const input = createTestInput();
+  const result = {
+    id: input.id,
+    name: `${input.first} ${input.last}`,
+  };
+  return { input, result };
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/test-with-vitest/coverage/lcov-report/src/data/index.html b/test-with-vitest/coverage/lcov-report/src/data/index.html new file mode 100644 index 00000000..eab26ab5 --- /dev/null +++ b/test-with-vitest/coverage/lcov-report/src/data/index.html @@ -0,0 +1,161 @@ + + + + + + Code coverage report for src/data + + + + + + + + + +
+
+

All files src/data

+
+ +
+ 32.14% + Statements + 9/28 +
+ + +
+ 20% + Branches + 2/10 +
+ + +
+ 50% + Functions + 5/10 +
+ + +
+ 32.14% + Lines + 9/28 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
connect-to-cosmos.ts +
+
0%0/11100%0/00%0/30%0/11
fake-data.ts +
+
100%7/7100%0/0100%3/3100%7/7
model.ts +
+
25%2/820%2/1066.66%2/325%2/8
verify.ts +
+
0%0/2100%0/00%0/10%0/2
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/test-with-vitest/coverage/lcov-report/src/data/model.ts.html b/test-with-vitest/coverage/lcov-report/src/data/model.ts.html new file mode 100644 index 00000000..a80af4be --- /dev/null +++ b/test-with-vitest/coverage/lcov-report/src/data/model.ts.html @@ -0,0 +1,202 @@ + + + + + + Code coverage report for src/data/model.ts + + + + + + + + + +
+
+

All files / src/data model.ts

+
+ +
+ 25% + Statements + 2/8 +
+ + +
+ 20% + Branches + 2/10 +
+ + +
+ 66.66% + Functions + 2/3 +
+ + +
+ 25% + Lines + 2/8 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
// input-verified.ts
+export interface DbDocument {
+  id: string;
+  name: string;
+}
+export interface DbError {
+  message: string;
+  code: number;
+}
+export interface VerificationErrors {
+  message: string;
+}
+export interface RawInput {
+  id: string;
+  first: string;
+  last: string;
+}
+ 
+export function isDbError(error: any): error is DbError {
+  return 'message' in error && 'code' in error;
+}
+export function isVerificationErrors(error: any): error is VerificationErrors {
+  return 'message' in error;
+}
+export function validateRawInput(input: any): string[] {
+  const errors: string[] = [];
+ 
+  // eslint-disable-next-line @typescript-eslint/no-unsafe-call
+  if (typeof input.first !== 'string' || input.first.trim().length === 0) {
+    errors.push('First name is required and must be a non-empty string');
+  }
+ 
+  // eslint-disable-next-line @typescript-eslint/no-unsafe-call
+  if (typeof input.last !== 'string' || input.last.trim().length === 0) {
+    errors.push('Last name is required and must be a non-empty string');
+  }
+ 
+  return errors;
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/test-with-vitest/coverage/lcov-report/src/data/verify.ts.html b/test-with-vitest/coverage/lcov-report/src/data/verify.ts.html new file mode 100644 index 00000000..bdee114d --- /dev/null +++ b/test-with-vitest/coverage/lcov-report/src/data/verify.ts.html @@ -0,0 +1,112 @@ + + + + + + Code coverage report for src/data/verify.ts + + + + + + + + + +
+
+

All files / src/data verify.ts

+
+ +
+ 0% + Statements + 0/2 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 0% + Lines + 0/2 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10  +  +  +  +  +  +  +  +  + 
// input-verified.ts
+import { validateRawInput } from './model.js';
+ 
+export default class Verfiy {
+  static inputVerified(doc: any): boolean {
+    const result = validateRawInput(doc);
+    return result.length === 0;
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/test-with-vitest/coverage/lcov-report/src/index.html b/test-with-vitest/coverage/lcov-report/src/index.html new file mode 100644 index 00000000..629fe7b3 --- /dev/null +++ b/test-with-vitest/coverage/lcov-report/src/index.html @@ -0,0 +1,116 @@ + + + + + + Code coverage report for src + + + + + + + + + +
+
+

All files src

+
+ +
+ 0% + Statements + 0/7 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/3 +
+ + +
+ 0% + Lines + 0/7 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
index.ts +
+
0%0/7100%0/00%0/30%0/7
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/test-with-vitest/coverage/lcov-report/src/index.ts.html b/test-with-vitest/coverage/lcov-report/src/index.ts.html new file mode 100644 index 00000000..d2190b70 --- /dev/null +++ b/test-with-vitest/coverage/lcov-report/src/index.ts.html @@ -0,0 +1,136 @@ + + + + + + Code coverage report for src/index.ts + + + + + + + + + +
+
+

All files / src index.ts

+
+ +
+ 0% + Statements + 0/7 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 0% + Functions + 0/3 +
+ + +
+ 0% + Lines + 0/7 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
import CosmosConnector from './data/connect-to-cosmos.js';
+import { createTestInput } from './data/fake-data.js';
+import type { DbDocument, DbError, VerificationErrors } from './data/model.js';
+import { insertDocument } from './lib/insert.js';
+ 
+async function main(): Promise<DbDocument | DbError | VerificationErrors> {
+  const container = await CosmosConnector.connectToContainer();
+  const input = createTestInput();
+  return await insertDocument(container, input);
+}
+ 
+main()
+  .then((doc) => console.log(doc))
+  .catch((error) => {
+    console.error(error);
+    process.exit(1);
+  });
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/test-with-vitest/coverage/lcov-report/src/lib/index.html b/test-with-vitest/coverage/lcov-report/src/lib/index.html new file mode 100644 index 00000000..bc5f95a7 --- /dev/null +++ b/test-with-vitest/coverage/lcov-report/src/lib/index.html @@ -0,0 +1,116 @@ + + + + + + Code coverage report for src/lib + + + + + + + + + +
+
+

All files src/lib

+
+ +
+ 72.72% + Statements + 8/11 +
+ + +
+ 50% + Branches + 3/6 +
+ + +
+ 100% + Functions + 1/1 +
+ + +
+ 72.72% + Lines + 8/11 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
insert.ts +
+
72.72%8/1150%3/6100%1/172.72%8/11
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/test-with-vitest/coverage/lcov-report/src/lib/insert.ts.html b/test-with-vitest/coverage/lcov-report/src/lib/insert.ts.html new file mode 100644 index 00000000..0641e1d3 --- /dev/null +++ b/test-with-vitest/coverage/lcov-report/src/lib/insert.ts.html @@ -0,0 +1,208 @@ + + + + + + Code coverage report for src/lib/insert.ts + + + + + + + + + +
+
+

All files / src/lib insert.ts

+
+ +
+ 72.72% + Statements + 8/11 +
+ + +
+ 50% + Branches + 3/6 +
+ + +
+ 100% + Functions + 1/1 +
+ + +
+ 72.72% + Lines + 8/11 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42  +  +  +  +  +  +  +  +  +  +  +  +  +  +3x +  +3x +1x +  +  +2x +2x +  +  +  +  +1x +  +1x +  +  +  +  +  +  +  +  +1x +  +  +  + 
// insertDocument.ts
+import { Container } from '../data/connect-to-cosmos.js';
+import type {   
+  DbDocument,
+  DbError, 
+  RawInput,
+  VerificationErrors, 
+} from '../data/model.js';
+import Verify from '../data/verify.js';
+ 
+export async function insertDocument(
+  container: Container,
+  doc: RawInput,
+): Promise<DbDocument | DbError | VerificationErrors> {
+  const isVerified: boolean = Verify.inputVerified(doc);
+ 
+  if (!isVerified) {
+    return { message: 'Verification failed' } as VerificationErrors;
+  }
+ 
+  try {
+    const { resource } = await container.items.create({
+      id: doc.id,
+      name: `${doc.first} ${doc.last}`,
+    });
+ 
+    return resource as DbDocument;
+  } catch (error: any) {
+    Iif (error instanceof Error) {
+      if ((error as any).code === 409) {
+        return {
+          message: 'Insertion failed: Duplicate entry',
+          code: 409,
+        } as DbError;
+      }
+      return { message: error.message, code: (error as any).code } as DbError;
+    } else {
+      return { message: 'An unknown error occurred', code: 500 } as DbError;
+    }
+  }
+}
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/test-with-vitest/coverage/lcov.info b/test-with-vitest/coverage/lcov.info new file mode 100644 index 00000000..574cefa1 --- /dev/null +++ b/test-with-vitest/coverage/lcov.info @@ -0,0 +1,144 @@ +TN: +SF:src/index.ts +FN:6,main +FN:13,(anonymous_1) +FN:14,(anonymous_2) +FNF:3 +FNH:0 +FNDA:0,main +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +DA:7,0 +DA:8,0 +DA:9,0 +DA:12,0 +DA:13,0 +DA:15,0 +DA:16,0 +LF:7 +LH:0 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src/data/connect-to-cosmos.ts +FN:11,(anonymous_0) +FN:18,(anonymous_1) +FN:36,(anonymous_2) +FNF:3 +FNH:0 +FNDA:0,(anonymous_0) +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +DA:12,0 +DA:13,0 +DA:14,0 +DA:15,0 +DA:19,0 +DA:20,0 +DA:21,0 +DA:24,0 +DA:29,0 +DA:33,0 +DA:37,0 +LF:11 +LH:0 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src/data/fake-data.ts +FN:5,createFixture +FN:13,createTestInput +FN:18,createTestInputAndResult +FNF:3 +FNH:3 +FNDA:2,createFixture +FNDA:2,createTestInput +FNDA:2,createTestInputAndResult +DA:6,2 +DA:10,2 +DA:14,2 +DA:15,2 +DA:22,2 +DA:23,2 +DA:27,2 +LF:7 +LH:7 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src/data/model.ts +FN:19,isDbError +FN:22,isVerificationErrors +FN:25,validateRawInput +FNF:3 +FNH:2 +FNDA:1,isDbError +FNDA:1,isVerificationErrors +FNDA:0,validateRawInput +DA:20,1 +DA:23,1 +DA:26,0 +DA:29,0 +DA:30,0 +DA:34,0 +DA:35,0 +DA:38,0 +LF:8 +LH:2 +BRDA:20,0,0,1 +BRDA:20,0,1,1 +BRDA:29,1,0,0 +BRDA:29,1,1,0 +BRDA:29,2,0,0 +BRDA:29,2,1,0 +BRDA:34,3,0,0 +BRDA:34,3,1,0 +BRDA:34,4,0,0 +BRDA:34,4,1,0 +BRF:10 +BRH:2 +end_of_record +TN: +SF:src/data/verify.ts +FN:5,(anonymous_0) +FNF:1 +FNH:0 +FNDA:0,(anonymous_0) +DA:6,0 +DA:7,0 +LF:2 +LH:0 +BRF:0 +BRH:0 +end_of_record +TN: +SF:src/lib/insert.ts +FN:11,insertDocument +FNF:1 +FNH:1 +FNDA:3,insertDocument +DA:15,3 +DA:17,3 +DA:18,1 +DA:21,2 +DA:22,2 +DA:27,1 +DA:29,1 +DA:30,0 +DA:31,0 +DA:36,0 +DA:38,1 +LF:11 +LH:8 +BRDA:17,0,0,1 +BRDA:17,0,1,2 +BRDA:29,1,0,0 +BRDA:29,1,1,1 +BRDA:30,2,0,0 +BRDA:30,2,1,0 +BRF:6 +BRH:3 +end_of_record diff --git a/test-with-vitest/package.json b/test-with-vitest/package.json new file mode 100644 index 00000000..31c8cc7c --- /dev/null +++ b/test-with-vitest/package.json @@ -0,0 +1,26 @@ +{ + "name": "test-with-vitest", + "version": "1.0.0", + "main": "index.js", + "type": "module", + "scripts": { + "build": "rm -rf dist && tsc", + "test": "vitest run --coverage", + "test2": "node --trace-exit ./node_modules/vitest/vitest.js run --coverage" + }, + "keywords": [], + "author": "", + "license": "ISC", + "description": "", + "devDependencies": { + "@vitest/coverage-istanbul": "^3.0.8", + "vitest": "^3.0.8" + }, + "dependencies": { + "@azure/cosmos": "^4.1.0", + "@azure/identity": "^4.4.1", + "@faker-js/faker": "^8.4.1", + "dotenv": "^16.4.5", + "uuid": "^10.0.0" + } +} diff --git a/testrunner-typescript/src/app/data/connect-to-cosmos.ts b/test-with-vitest/src/data/connect-to-cosmos.ts similarity index 100% rename from testrunner-typescript/src/app/data/connect-to-cosmos.ts rename to test-with-vitest/src/data/connect-to-cosmos.ts diff --git a/testrunner-sha/src/app/data/fake-data.ts b/test-with-vitest/src/data/fake-data.ts similarity index 91% rename from testrunner-sha/src/app/data/fake-data.ts rename to test-with-vitest/src/data/fake-data.ts index 6deaa413..82c90b66 100644 --- a/testrunner-sha/src/app/data/fake-data.ts +++ b/test-with-vitest/src/data/fake-data.ts @@ -1,6 +1,6 @@ import { v4 as uuidv4 } from 'uuid'; import { faker } from '@faker-js/faker'; -import { DbDocument, RawInput } from './model.ts'; +import type { DbDocument, RawInput } from './model.js'; function createFixture(): T { const result = { diff --git a/testrunner-typescript/src/app/data/model.ts b/test-with-vitest/src/data/model.ts similarity index 100% rename from testrunner-typescript/src/app/data/model.ts rename to test-with-vitest/src/data/model.ts diff --git a/testrunner-typescript/src/app/data/verify.ts b/test-with-vitest/src/data/verify.ts similarity index 78% rename from testrunner-typescript/src/app/data/verify.ts rename to test-with-vitest/src/data/verify.ts index 3cb435e6..67cba7c6 100644 --- a/testrunner-typescript/src/app/data/verify.ts +++ b/test-with-vitest/src/data/verify.ts @@ -1,5 +1,5 @@ // input-verified.ts -import { validateRawInput } from './model'; +import { validateRawInput } from './model.js'; export default class Verfiy { static inputVerified(doc: any): boolean { diff --git a/testrunner-typescript/src/app/index.ts b/test-with-vitest/src/index.ts similarity index 69% rename from testrunner-typescript/src/app/index.ts rename to test-with-vitest/src/index.ts index 3c81fe0a..cf9b01ac 100644 --- a/testrunner-typescript/src/app/index.ts +++ b/test-with-vitest/src/index.ts @@ -1,7 +1,7 @@ -import CosmosConnector from './data/connect-to-cosmos'; -import { createTestInput } from './data/fake-data'; -import type { DbDocument, DbError, VerificationErrors } from './data/model'; -import { insertDocument } from './lib/insert'; +import CosmosConnector from './data/connect-to-cosmos.js'; +import { createTestInput } from './data/fake-data.js'; +import type { DbDocument, DbError, VerificationErrors } from './data/model.js'; +import { insertDocument } from './lib/insert.js'; async function main(): Promise { const container = await CosmosConnector.connectToContainer(); diff --git a/testrunner-typescript/src/app/lib/insert.ts b/test-with-vitest/src/lib/insert.ts similarity index 88% rename from testrunner-typescript/src/app/lib/insert.ts rename to test-with-vitest/src/lib/insert.ts index f5d99b10..e873cadd 100644 --- a/testrunner-typescript/src/app/lib/insert.ts +++ b/test-with-vitest/src/lib/insert.ts @@ -1,12 +1,12 @@ // insertDocument.ts -import { Container } from '../data/connect-to-cosmos'; +import { Container } from '../data/connect-to-cosmos.js'; import type { DbDocument, DbError, RawInput, VerificationErrors, -} from '../data/model'; -import Verify from '../data/verify'; +} from '../data/model.js'; +import Verify from '../data/verify.js'; export async function insertDocument( container: Container, diff --git a/test-with-vitest/tests/01-spies.test.ts b/test-with-vitest/tests/01-spies.test.ts new file mode 100644 index 00000000..3ce68d1d --- /dev/null +++ b/test-with-vitest/tests/01-spies.test.ts @@ -0,0 +1,18 @@ +import { describe, it, expect, vi } from 'vitest'; + +function run({ fn, times }: { fn: (arg: { current: number }) => void; times: number }) { + for (let i = 0; i < times; i++) { + fn({ current: i * 5 }); + } +} + + + it('Spies: should verify calls in a mock', () => { + const spy = vi.fn(); + run({ fn: spy, times: 3 }); + + expect(spy).toHaveBeenCalledTimes(3); + expect(spy.mock.calls[0][0]).toEqual({ current: 0 }); + expect(spy.mock.calls[1][0]).toEqual({ current: 5 }); + expect(spy.mock.calls[2][0]).toEqual({ current: 10 }); + }); diff --git a/test-with-vitest/tests/02-stubs.test.ts b/test-with-vitest/tests/02-stubs.test.ts new file mode 100644 index 00000000..9b5f4fb7 --- /dev/null +++ b/test-with-vitest/tests/02-stubs.test.ts @@ -0,0 +1,88 @@ +import { describe, it, beforeEach, afterEach, expect, vi } from 'vitest'; + +class Service { + static async getTalks({ skip, limit }: { skip: number; limit: number }): Promise { + // Use a public API (jsonplaceholder) to retrieve posts with pagination. + const url = `https://jsonplaceholder.typicode.com/posts?_start=${skip}&_limit=${limit}`; + const response = await fetch(url); + return response.json(); + } +} + +function mapResponse(data: any[]): string { + return data + .map(({ id, title }, index) => `[${index}] id: ${id}, title: ${title}`) + .join('\n'); +} + +async function run({ skip = 0, limit = 10 } = {}): Promise { + const talks = mapResponse(await Service.getTalks({ skip, limit })); + return talks; +} + +describe('Stub Test Suite', () => { + beforeEach(() => { + vi.restoreAllMocks(); + }); + + afterEach(() => { + // Optional cleanup if needed. + }); + + it('should stub APIs', async () => { + // Stub Service.getTalks so that it returns one fake post. + const m = vi.spyOn(Service, 'getTalks'); + m.mockImplementation(async () => [ + { + id: '1', + title: 'Sample Post' + } + ]); + + const result = await run({ limit: 1 }); + const expected = `[0] id: 1, title: Sample Post`; + + // Verify that the stubbed method was called once. + expect(m).toHaveBeenCalledTimes(1); + expect(m.mock.calls[0][0]).toEqual({ skip: 0, limit: 1 }); + expect(result).toBe(expected); + }); + + it('should stub different values for API calls', async () => { + // Instead of chaining multiple mockImplementationOnce calls, build a responses array: + const m = vi.spyOn(Service, 'getTalks'); + const responses = [ + async () => [{ id: '1', title: 'Post One' }], + async () => [{ id: '2', title: 'Post Two' }], + async () => [{ id: '3', title: 'Post Three' }] + ]; + + m.mockImplementation(async (args) => { + // Return the next response from the array. + const fn = responses.shift(); + return fn ? await fn() : []; + }); + + { + const result = await run({ skip: 0, limit: 1 }); + const expected = `[0] id: 1, title: Post One`; + expect(result).toBe(expected); + } + { + const result = await run({ skip: 1, limit: 1 }); + const expected = `[0] id: 2, title: Post Two`; + expect(result).toBe(expected); + } + { + const result = await run({ skip: 2, limit: 1 }); + const expected = `[0] id: 3, title: Post Three`; + expect(result).toBe(expected); + } + + // Verify call count and arguments. + expect(m).toHaveBeenCalledTimes(3); + expect(m.mock.calls[0][0]).toEqual({ skip: 0, limit: 1 }); + expect(m.mock.calls[1][0]).toEqual({ skip: 1, limit: 1 }); + expect(m.mock.calls[2][0]).toEqual({ skip: 2, limit: 1 }); + }); +}); \ No newline at end of file diff --git a/test-with-vitest/tests/boilerplate-with-mock.test.ts b/test-with-vitest/tests/boilerplate-with-mock.test.ts new file mode 100644 index 00000000..2794142e --- /dev/null +++ b/test-with-vitest/tests/boilerplate-with-mock.test.ts @@ -0,0 +1,29 @@ +import { describe, it, beforeEach, afterEach, expect, vi } from 'vitest'; + +const result = 1; + +class MyService { + static async myFunction() { + return Promise.resolve(result); + } +} + +describe('boilerplate with mock', () => { + beforeEach(() => { + // Restore all mocks before each test. + vi.restoreAllMocks(); + }); + + afterEach(() => { + // Cleanup required after each test (if needed). + }); + + it('should if ', async () => { + // Replace the original implementation with a mock, returning 2. + vi.spyOn(MyService, 'myFunction').mockResolvedValue(2); + + // Test the function: it should now return the mocked value. + const resultVal = await MyService.myFunction(); + expect(resultVal).toBe(2); + }); +}); \ No newline at end of file diff --git a/test-with-vitest/tests/boilerplate.test.ts b/test-with-vitest/tests/boilerplate.test.ts new file mode 100644 index 00000000..7d604114 --- /dev/null +++ b/test-with-vitest/tests/boilerplate.test.ts @@ -0,0 +1,26 @@ +import { describe, it, beforeEach, afterEach, expect } from 'vitest'; + +describe('boilerplate', () => { + beforeEach(() => { + // Setup required before each test + }); + + afterEach(() => { + // Cleanup required after each test + }); + + it('should if ', async () => { + // Arrange + // - set up the test data and the expected result + + // Act + // - call the function to test + + // Assert + // - check the state: result returned from function + // - check the behavior: dependency function calls + + // Example assertion: + expect(true).toBe(true); + }); +}); \ No newline at end of file diff --git a/test-with-vitest/tests/fake-in-mem-db.test.ts b/test-with-vitest/tests/fake-in-mem-db.test.ts new file mode 100644 index 00000000..4f89cef8 --- /dev/null +++ b/test-with-vitest/tests/fake-in-mem-db.test.ts @@ -0,0 +1,63 @@ +import { describe, it, beforeEach, afterEach, expect, vi } from 'vitest'; + +class FakeDatabase { + private data: Record; + + constructor() { + this.data = {}; + } + + save(key: string, value: any): void { + this.data[key] = value; + } + + get(key: string): any { + return this.data[key]; + } +} + +// Function to test +function someTestFunction(db: FakeDatabase, key: string, value: any): any { + db.save(key, value); + return db.get(key); +} + +describe('In-Mem DB', () => { + let fakeDb: FakeDatabase; + let testKey: string; + let testValue: any; + let saveSpy: ReturnType; + + beforeEach(() => { + fakeDb = new FakeDatabase(); + testKey = 'testKey'; + testValue = { + first: 'John', + last: 'Jones', + lastUpdated: new Date().toISOString(), + }; + + // Create a spy on the save method. + saveSpy = vi.spyOn(fakeDb, 'save'); + }); + + afterEach(() => { + // Restore the spied methods. + vi.restoreAllMocks(); + }); + + it('should save and return the correct value', () => { + // Call the function under test. + const result = someTestFunction(fakeDb, testKey, testValue); + + // Verify state. + expect(result).toEqual(testValue); + expect(result.first).toBe('John'); + expect(result.last).toBe('Jones'); + expect(result.lastUpdated).toBe(testValue.lastUpdated); + + // Verify behavior using vi.spyOn. + expect(saveSpy).toHaveBeenCalledTimes(1); + expect(saveSpy).toHaveBeenCalledWith(testKey, testValue); + }); +}); \ No newline at end of file diff --git a/test-with-vitest/tests/insert.test.ts b/test-with-vitest/tests/insert.test.ts new file mode 100644 index 00000000..0ee73478 --- /dev/null +++ b/test-with-vitest/tests/insert.test.ts @@ -0,0 +1,127 @@ +import { describe, it, beforeEach, expect, vi, Mock } from 'vitest'; +import { Container } from '../src/data/connect-to-cosmos.js'; +import { createTestInputAndResult } from '../src/data/fake-data.js'; +import { + DbDocument, + DbError, + isDbError, + isVerificationErrors, + RawInput, +} from '../src/data/model.js'; +import Verify from '../src/data/verify.js'; +import { insertDocument } from '../src/lib/insert.js'; + +// Mock app dependencies for Cosmos DB setup +vi.mock('../app/data/connect-to-cosmos', () => ({ + connectToContainer: vi.fn(), + getUniqueId: vi.fn().mockReturnValue('unique-id'), +})); + +// Mock app dependencies for input verification +vi.mock('../app/data/verify', () => { + return { + default: class { + static inputVerified = vi.fn(); + }, + }; +}); + +describe('SDK', () => { + // Declare the Cosmos DB Container mock. + let mockContainer: Container; + + beforeEach(() => { + // Clear all mocks before each test. + vi.clearAllMocks(); + + // Create a fake container with a mocked create method. + mockContainer = { + items: { + create: vi.fn(), + }, + } as unknown as Container; + }); + + it('should return verification error if input is not verified', async () => { + // Arrange – mock the input verification function to return false. + vi.spyOn(Verify, "inputVerified").mockReturnValue(false); + // Arrange – provide a doc with an incorrect shape. + const doc = { name: 'test' }; + + // Act – call the function under test. + const insertDocumentResult = await insertDocument( + mockContainer, + doc as unknown as RawInput, + ); + + // Assert – state verification: result should indicate verification failure. + if (isVerificationErrors(insertDocumentResult)) { + expect(insertDocumentResult).toEqual({ + message: 'Verification failed', + }); + } else { + throw new Error('Result is not of type VerificationErrors'); + } + + // Assert – behavior verification: ensure create method was not called. + expect((mockContainer.items.create as vi.Mock)).not.toHaveBeenCalled(); + }); + + it('should insert document successfully', async () => { + // Arrange – create input and expected result data. + const { input, result }: { input: RawInput; result: Partial } = + createTestInputAndResult(); + + // Arrange – mock the input verification function to return true. + vi.spyOn(Verify, "inputVerified").mockReturnValue(true); + + // Arrange – mock the create method to resolve with our expected result. + const mockedCreate = mockContainer.items.create as unknown as Mock; + mockedCreate.mockImplementation(async () => ({ resource: result })); + + // Act – call the function under test. + const insertDocumentResult = await insertDocument(mockContainer, input); + + // Assert – state verification: check the result is as expected. + expect(insertDocumentResult).toEqual(result); + + // Assert – behavior verification: ensure create was called once with correct arguments. + expect(mockedCreate).toHaveBeenCalledTimes(1); + expect(mockedCreate.mock.calls[0][0]).toEqual({ + id: input.id, + name: result.name, + }); + }); + + it('should return error if db insert fails', async () => { + // Arrange – create input and expected result data. + const { input, result } = createTestInputAndResult(); + + // Arrange – mock the input verification to return true. + vi.spyOn(Verify, "inputVerified").mockReturnValue(true); + + // Arrange – mock the create method to reject with an error. + const mockError: DbError = { + message: 'An unknown error occurred', + code: 500, + }; + (mockContainer.items.create as vi.Mock).mockRejectedValue(mockError); + + // Act – call the function under test. + const insertDocumentResult = await insertDocument(mockContainer, input); + + // Assert – verify result is of type DbError. + if (isDbError(insertDocumentResult)) { + expect(insertDocumentResult.message).toBe(mockError.message); + } else { + throw new Error('Result is not of type DbError'); + } + + // Assert – behavior verification: ensure create was called once with correct arguments. + expect((mockContainer.items.create as vi.Mock)).toHaveBeenCalledTimes(1); + expect((mockContainer.items.create as vi.Mock).mock.calls[0][0]).toEqual({ + id: input.id, + name: result.name, + }); + }); +}); \ No newline at end of file diff --git a/test-with-vitest/tsconfig.json b/test-with-vitest/tsconfig.json new file mode 100644 index 00000000..baa02259 --- /dev/null +++ b/test-with-vitest/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "ESNext", // Specify ECMAScript target version + "module": "NodeNext", // Specify module code generation + "strict": true, // Enable all strict type-checking options + "esModuleInterop": true, // Enables emit interoperability between CommonJS and ES Modules + "noImplicitAny": false, // Enable error reporting for expressions and declarations with an implied 'any' type + "forceConsistentCasingInFileNames": true, // Disallow inconsistently-cased references to the same file + "outDir": "./dist", + "rootDir": "./", + "erasableSyntaxOnly": true, + }, + "include": ["src", "tests"], + "exclude": [ + "node_modules" + ] +} \ No newline at end of file diff --git a/test-with-vitest/vitest.config.ts b/test-with-vitest/vitest.config.ts new file mode 100644 index 00000000..c9ae13c3 --- /dev/null +++ b/test-with-vitest/vitest.config.ts @@ -0,0 +1,17 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + environment: 'node', + include: ['tests/**/*.test.ts'], + coverage: { + provider: 'istanbul', // Alternatively, you can use "c8" + reporter: ['text', 'lcov'], + // Optionally add thresholds here: + // lines: 80, + // functions: 80, + // branches: 80, + // statements: 80, + }, + }, +}); \ No newline at end of file diff --git a/testrunner-sha/README.md b/testrunner-sha/README.md deleted file mode 100644 index 376bbee7..00000000 --- a/testrunner-sha/README.md +++ /dev/null @@ -1,29 +0,0 @@ -# Unit testing for the Azure SDK for JavaScript - -This subfolder is the source code for the [TBD article](). The purpose is to demonstrate unit test mocks for the Azure SDK for JavaScript. These specific examples use Azure Cosmos DB. - -## To run the test - -1. `npm install` -2. `npm run build` -3. `npm test` - - ```console - > unit-testing@1.0.0 test - > jest dist - - PASS dist/fakes/fake-in-mem-db.spec.js - PASS dist/mock-function/lib/insert.spec.js - - Test Suites: 2 passed, 2 total - Tests: 4 passed, 4 total - Snapshots: 0 total - Time: 4.247 s, estimated 5 s - Ran all test suites matching /dist/i. - ``` - -## Related content - -* [Passwordless connections for Azure services](https://learn.microsoft.com/azure/developer/intro/passwordless-overview) -* [Cosmos DB keyless access to the service](https://learn.microsoft.com/azure/cosmos-db/role-based-access-control) -* [Node.js Test Runner](https://nodejs.org/docs/latest/api/test.html#test-runner) \ No newline at end of file diff --git a/testrunner-sha/package-lock.json b/testrunner-sha/package-lock.json deleted file mode 100644 index 613ce82c..00000000 --- a/testrunner-sha/package-lock.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "name": "testrunner", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "testrunner", - "version": "1.0.0", - "dependencies": { - "@types/node": "^22.13.10" - } - }, - "node_modules/@types/node": { - "version": "22.13.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.10.tgz", - "integrity": "sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==", - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "license": "MIT" - } - } -} diff --git a/testrunner-sha/package.json b/testrunner-sha/package.json deleted file mode 100644 index 5ef567f1..00000000 --- a/testrunner-sha/package.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "testrunner-native", - "version": "1.0.0", - "main": "index.js", - "type": "module", - "scripts": { - "build": "rm -rf dist && tsc", - "test": "node --test --experimental-test-coverage --experimental-test-module-mocks", - "test:dev": "npm run build && node --watch --test test/ " - }, - "directories": { - "test": "test" - }, - "dependencies": { - "@types/node": "^22.13.10" - } -} diff --git a/testrunner-sha/src/app/lib/insert.test.ts b/testrunner-sha/src/app/lib/insert.test.ts deleted file mode 100644 index cc28563a..00000000 --- a/testrunner-sha/src/app/lib/insert.test.ts +++ /dev/null @@ -1,145 +0,0 @@ -// insertDocument.test.ts -import assert from 'node:assert'; -import { beforeEach, describe, it, mock } from 'node:test'; -import { Container } from '../data/connect-to-cosmos.ts'; -import { createTestInputAndResult } from '../data/fake-data.ts'; -import type { - DbDocument, - DbError, - RawInput -} from '../data/model.ts'; -import { - isDbError, - isVerificationErrors -} from '../data/model.ts'; -// Instead of jest.mock, import the whole module to override functions as needed. -import CosmosConnector from '../data/connect-to-cosmos.ts'; -import Verify from '../data/verify.ts'; -import { insertDocument } from './insert.ts'; - -// --- Test suite for insertDocument --- -describe('Insert into db', () => { - - // Set up a fresh container object before each test. - beforeEach(() => { - // Setup required before each test - mock.restoreAll() - }) - - it("Success", () => { - it('should insert document successfully', async () => { - // Arrange: override inputVerified to return true. - const { input, result }: { input: RawInput; result: Partial } = createTestInputAndResult(); - - const fakeContainer = { - items: { - create: async (doc: any) => { - return { resource: result }; - }, - }, - } as unknown as Container; - - const mVerify = mock.method(Verify, "inputVerified").mock; - mVerify.mockImplementation(() => true); - - const mContainerCreate = mock.method(fakeContainer.items as any, "create").mock; - mContainerCreate.mockImplementation(async (doc: any) => { - return { resource: result }; - }); - - // Act: - const receivedResult = await insertDocument(fakeContainer, input); - - // Assert - State verification: Ensure the result is as expected. - assert.deepStrictEqual(receivedResult, result); - - // Assert - Behavior verification: Ensure create was called once with correct arguments. - assert.strictEqual(mContainerCreate.callCount(), 1); - assert.deepStrictEqual(mContainerCreate.calls[0].arguments[0], { - id: input.id, - name: result.name, - }); - }); - }); - - it("Failure", () => { - it('should return verification error if input is not verified', async () => { - - const fakeContainer = { - items: { - create: async (_: any) => { - throw new Error('Create method not implemented'); - }, - }, - } as unknown as Container; - - const mVerify = mock.method(Verify, "inputVerified").mock; - mVerify.mockImplementation(() => false); - - const mGetUniqueId = mock.method(CosmosConnector, "getUniqueId").mock; - mGetUniqueId.mockImplementation(() => 'unique-id'); - - const mContainerCreate = mock.method(fakeContainer.items, "create").mock; - - // Arrange: wrong shape of document on purpose. - const doc = { name: 'test' } as unknown as RawInput; - - // Act: - const insertDocumentResult = await insertDocument(fakeContainer, doc); - - // Assert - State verification. - if (isVerificationErrors(insertDocumentResult)) { - assert.deepStrictEqual(insertDocumentResult, { message: 'Verification failed' }); - } else { - throw new Error('Result is not of type VerificationErrors'); - } - - // Assert - Behavior verification: Verify that create was never called. - assert.strictEqual(mContainerCreate.callCount(), 0); - - }); - }); - - it('should return error if db insert fails', async () => { - // Arrange: override inputVerified to return true. - const { input, result } = createTestInputAndResult(); - let errorMessage: string = 'An unknown error occurred'; - - const fakeContainer = { - items: { - create: async (doc: any): Promise => { - return Promise.resolve(null); - }, - }, - } as unknown as Container; - - const mVerify = mock.method(Verify, "inputVerified").mock; - mVerify.mockImplementation(() => true); - - const mContainerCreate = mock.method(fakeContainer.items, "create").mock; - mContainerCreate.mockImplementation = async (doc: any) => { - const mockError: DbError = { - message: errorMessage, - code: 500, - }; - throw mockError; - } - - // Act: - const insertDocumentResult = await insertDocument(fakeContainer, input); - - // Assert - Verify type as DbError. - if (isDbError(insertDocumentResult)) { - assert.strictEqual(insertDocumentResult.message, errorMessage); - } else { - throw new Error('Result is not of type DbError'); - } - - // Assert - Ensure create method was called once with the correct arguments. - assert.strictEqual(mContainerCreate.callCount(), 1); - assert.deepStrictEqual(mContainerCreate.calls[0].arguments[0], { - id: input.id, - name: result.name, - }); - }); -}); \ No newline at end of file diff --git a/testrunner-sha/test/basics/02-stubs.test.ts b/testrunner-sha/test/basics/02-stubs.test.ts deleted file mode 100644 index 207abfe4..00000000 --- a/testrunner-sha/test/basics/02-stubs.test.ts +++ /dev/null @@ -1,119 +0,0 @@ -import { - describe, - it, - beforeEach, - mock -} from 'node:test' -import assert from 'node:assert' - -class Service { - static async getTalks({ skip, limit }) { - const items = await fetch('https://tml-api.herokuapp.com/graphql', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - query: ` - { - getTalks (skip: ${skip}, limit: ${limit}) { - totalCount, - talks { - _id - title - } - } - } - ` - }) - }) - return (await items.json()).data.getTalks.talks - } -} - -function mapResponse(data) { - return data - .map(({ _id, title }, index) => `[${index}] id: ${_id}, title: ${title}`) - .join('\n') -} - -async function run({ skip = 0, limit = 10 }) { - const talks = mapResponse(await Service.getTalks({ skip, limit })) - return talks -} - -describe('Stub Test Suite', () => { - beforeEach(() => mock.restoreAll()) - - it('should stub APIs', async () => { - const m = mock.method( - Service, - "getTalks", - ).mock; - m.mockImplementation(async () => [ - { - _id: '63865750c839dbaacd8116e1', - title: 'The Journey About How I Fixed a Bug in the Node.js Core That Affected Thousands of Packages' - } - ]) - - const result = await run({ limit: 1 }) - const expected = `[0] id: 63865750c839dbaacd8116e1, title: The Journey About How I Fixed a Bug in the Node.js Core That Affected Thousands of Packages` - - assert.deepStrictEqual(m.callCount(), 1) - const calls = m.calls - - assert.deepStrictEqual(calls[0].arguments[0], { skip: 0, limit: 1 }) - assert.strictEqual(result, expected) - }) - - it('should stub different values for API calls', async () => { - const m = mock.method( - Service, - "getTalks", - ).mock - - m.mockImplementationOnce(async () => [ - { - _id: '63865750c839dbaacd8116e1', - title: 'The Journey About How I Fixed a Bug in the Node.js Core That Affected Thousands of Packages' - } - ], 0) - - m.mockImplementationOnce(async () => [ - { - _id: '01', - title: 'Mock 01' - } - ], 1) - - m.mockImplementationOnce(async () => [ - { - _id: '02', - title: 'Mock 02' - } - ], 2) - - { - const result = await run({ skip: 0, limit: 1 }) - const expected = `[0] id: 63865750c839dbaacd8116e1, title: The Journey About How I Fixed a Bug in the Node.js Core That Affected Thousands of Packages` - assert.strictEqual(result, expected) - } - { - const result = await run({ skip: 1, limit: 1 }) - const expected = `[0] id: 01, title: Mock 01` - assert.strictEqual(result, expected) - } - { - const result = await run({ skip: 2, limit: 1 }) - const expected = `[0] id: 02, title: Mock 02` - assert.strictEqual(result, expected) - } - - const calls = m.calls - assert.strictEqual(m.callCount(), 3) - assert.deepStrictEqual(calls[0].arguments[0], { skip: 0, limit: 1 }) - assert.deepStrictEqual(calls[1].arguments[0], { skip: 1, limit: 1 }) - assert.deepStrictEqual(calls[2].arguments[0], { skip: 2, limit: 1 }) - }) -}) \ No newline at end of file diff --git a/testrunner-sha/test/test-boilerplate/boilerplate-with-mock.test-2.ts b/testrunner-sha/test/test-boilerplate/boilerplate-with-mock.test-2.ts deleted file mode 100644 index 3d7614fb..00000000 --- a/testrunner-sha/test/test-boilerplate/boilerplate-with-mock.test-2.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { - describe, - it, - afterEach, - beforeEach, - mock - } from 'node:test' -import assert from 'node:assert'; - -import CosmosConnector from '../../src/app/data/connect-to-cosmos.ts'; - -describe('boilerplate with mock 2', () => { - beforeEach(() =>{ - // Setup required before each test - mock.restoreAll() - }) - afterEach(() => { - // Cleanup required after each test - }); - - it('should if ', async () => { - - const m = mock.method(CosmosConnector, "getUniqueId"); - m.mock.mockImplementation(() => { - // Replace the original implementation with a mock result - - // return fake guid - return '12345678-1234-1234-1234-123456789012' - - }); - - const result = await CosmosConnector.getUniqueId(); - assert.strictEqual(result, - '12345678-1234-1234-1234-123456789012' - ) - - }) - }) \ No newline at end of file diff --git a/testrunner-sha/tsconfig.json b/testrunner-sha/tsconfig.json deleted file mode 100644 index af542786..00000000 --- a/testrunner-sha/tsconfig.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "compilerOptions": { - "target": "es6", // Specify ECMAScript target version - "module": "commonjs", // Specify module code generation - "strict": true, // Enable all strict type-checking options - "esModuleInterop": true, // Enables emit interoperability between CommonJS and ES Modules - "noImplicitAny": false, // Enable error reporting for expressions and declarations with an implied 'any' type - "forceConsistentCasingInFileNames": true, // Disallow inconsistently-cased references to the same file - "outDir": "./dist", - "rootDir": "./", - "erasableSyntaxOnly": true, - "allowImportingTsExtensions": true, - "noEmit": true - }, - "include": [ - "src", - "test" - ], - "exclude": [ - "node_modules" - ] - } \ No newline at end of file diff --git a/testrunner-typescript/README.md b/testrunner-typescript/README.md deleted file mode 100644 index 376bbee7..00000000 --- a/testrunner-typescript/README.md +++ /dev/null @@ -1,29 +0,0 @@ -# Unit testing for the Azure SDK for JavaScript - -This subfolder is the source code for the [TBD article](). The purpose is to demonstrate unit test mocks for the Azure SDK for JavaScript. These specific examples use Azure Cosmos DB. - -## To run the test - -1. `npm install` -2. `npm run build` -3. `npm test` - - ```console - > unit-testing@1.0.0 test - > jest dist - - PASS dist/fakes/fake-in-mem-db.spec.js - PASS dist/mock-function/lib/insert.spec.js - - Test Suites: 2 passed, 2 total - Tests: 4 passed, 4 total - Snapshots: 0 total - Time: 4.247 s, estimated 5 s - Ran all test suites matching /dist/i. - ``` - -## Related content - -* [Passwordless connections for Azure services](https://learn.microsoft.com/azure/developer/intro/passwordless-overview) -* [Cosmos DB keyless access to the service](https://learn.microsoft.com/azure/cosmos-db/role-based-access-control) -* [Node.js Test Runner](https://nodejs.org/docs/latest/api/test.html#test-runner) \ No newline at end of file diff --git a/testrunner-typescript/package-lock.json b/testrunner-typescript/package-lock.json deleted file mode 100644 index 613ce82c..00000000 --- a/testrunner-typescript/package-lock.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "name": "testrunner", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "testrunner", - "version": "1.0.0", - "dependencies": { - "@types/node": "^22.13.10" - } - }, - "node_modules/@types/node": { - "version": "22.13.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.10.tgz", - "integrity": "sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==", - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "license": "MIT" - } - } -} diff --git a/testrunner-typescript/package.json b/testrunner-typescript/package.json deleted file mode 100644 index 5b9449f2..00000000 --- a/testrunner-typescript/package.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "name": "testrunner-typescript", - "version": "1.0.0", - "main": "index.js", - "scripts": { - "build": "rm -rf dist && tsc", - "test": "npm run build && node --test --experimental-test-coverage --experimental-test-module-mocks --trace-exit", - "test:dev": "npm run build && node --watch --test test/ " - }, - "directories": { - "test": "test" - }, - "dependencies": { - "@types/node": "^22.13.10" - } -} diff --git a/testrunner-typescript/src/test-app/boilerplate-with-mock.test-2.ts b/testrunner-typescript/src/test-app/boilerplate-with-mock.test-2.ts deleted file mode 100644 index a29022a3..00000000 --- a/testrunner-typescript/src/test-app/boilerplate-with-mock.test-2.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { - describe, - it, - afterEach, - beforeEach, - mock - } from 'node:test' -import assert from 'node:assert'; - -import CosmosConnector from '../app/data/connect-to-cosmos'; - -describe('boilerplate with mock 2', () => { - beforeEach(() =>{ - // Setup required before each test - mock.restoreAll() - }) - afterEach(() => { - // Cleanup required after each test - }); - - it('should if ', async () => { - - const m = mock.method(CosmosConnector, "getUniqueId"); - m.mock.mockImplementation(() => { - // Replace the original implementation with a mock result - - // return fake guid - return '12345678-1234-1234-1234-123456789012' - - }); - - const result = await CosmosConnector.getUniqueId(); - assert.strictEqual(result, - '12345678-1234-1234-1234-123456789012' - ) - - }) - }) \ No newline at end of file diff --git a/testrunner-typescript/src/test-basics/01-spies.test.ts b/testrunner-typescript/src/test-basics/01-spies.test.ts deleted file mode 100644 index 8bcbea13..00000000 --- a/testrunner-typescript/src/test-basics/01-spies.test.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { - describe, - it, - mock -} from 'node:test'; -import assert from 'node:assert'; - - -function run({fn, times}){ - for(let i = 0; i < times; i++){ - fn({current: i * 5}); - } -} - -describe('Spies', () => { - it('should verify calls in a mock', () => { - const spy = mock.fn(); - run({fn: spy, times: 3}); - - assert.strictEqual(spy.mock.callCount(), 3); - const calls = spy.mock.calls; - - assert.deepStrictEqual(calls[0].arguments[0], {current: 0}); - assert.deepStrictEqual(calls[1].arguments[0], {current: 5}); - assert.deepStrictEqual(calls[2].arguments[0], {current: 10}); - - }); -}); \ No newline at end of file diff --git a/testrunner-typescript/src/test-basics/02-stubs.test.ts b/testrunner-typescript/src/test-basics/02-stubs.test.ts deleted file mode 100644 index 207abfe4..00000000 --- a/testrunner-typescript/src/test-basics/02-stubs.test.ts +++ /dev/null @@ -1,119 +0,0 @@ -import { - describe, - it, - beforeEach, - mock -} from 'node:test' -import assert from 'node:assert' - -class Service { - static async getTalks({ skip, limit }) { - const items = await fetch('https://tml-api.herokuapp.com/graphql', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - query: ` - { - getTalks (skip: ${skip}, limit: ${limit}) { - totalCount, - talks { - _id - title - } - } - } - ` - }) - }) - return (await items.json()).data.getTalks.talks - } -} - -function mapResponse(data) { - return data - .map(({ _id, title }, index) => `[${index}] id: ${_id}, title: ${title}`) - .join('\n') -} - -async function run({ skip = 0, limit = 10 }) { - const talks = mapResponse(await Service.getTalks({ skip, limit })) - return talks -} - -describe('Stub Test Suite', () => { - beforeEach(() => mock.restoreAll()) - - it('should stub APIs', async () => { - const m = mock.method( - Service, - "getTalks", - ).mock; - m.mockImplementation(async () => [ - { - _id: '63865750c839dbaacd8116e1', - title: 'The Journey About How I Fixed a Bug in the Node.js Core That Affected Thousands of Packages' - } - ]) - - const result = await run({ limit: 1 }) - const expected = `[0] id: 63865750c839dbaacd8116e1, title: The Journey About How I Fixed a Bug in the Node.js Core That Affected Thousands of Packages` - - assert.deepStrictEqual(m.callCount(), 1) - const calls = m.calls - - assert.deepStrictEqual(calls[0].arguments[0], { skip: 0, limit: 1 }) - assert.strictEqual(result, expected) - }) - - it('should stub different values for API calls', async () => { - const m = mock.method( - Service, - "getTalks", - ).mock - - m.mockImplementationOnce(async () => [ - { - _id: '63865750c839dbaacd8116e1', - title: 'The Journey About How I Fixed a Bug in the Node.js Core That Affected Thousands of Packages' - } - ], 0) - - m.mockImplementationOnce(async () => [ - { - _id: '01', - title: 'Mock 01' - } - ], 1) - - m.mockImplementationOnce(async () => [ - { - _id: '02', - title: 'Mock 02' - } - ], 2) - - { - const result = await run({ skip: 0, limit: 1 }) - const expected = `[0] id: 63865750c839dbaacd8116e1, title: The Journey About How I Fixed a Bug in the Node.js Core That Affected Thousands of Packages` - assert.strictEqual(result, expected) - } - { - const result = await run({ skip: 1, limit: 1 }) - const expected = `[0] id: 01, title: Mock 01` - assert.strictEqual(result, expected) - } - { - const result = await run({ skip: 2, limit: 1 }) - const expected = `[0] id: 02, title: Mock 02` - assert.strictEqual(result, expected) - } - - const calls = m.calls - assert.strictEqual(m.callCount(), 3) - assert.deepStrictEqual(calls[0].arguments[0], { skip: 0, limit: 1 }) - assert.deepStrictEqual(calls[1].arguments[0], { skip: 1, limit: 1 }) - assert.deepStrictEqual(calls[2].arguments[0], { skip: 2, limit: 1 }) - }) -}) \ No newline at end of file diff --git a/testrunner-typescript/src/test-boilerplate/boilerplate-with-mock.test.ts b/testrunner-typescript/src/test-boilerplate/boilerplate-with-mock.test.ts deleted file mode 100644 index da8e0f5a..00000000 --- a/testrunner-typescript/src/test-boilerplate/boilerplate-with-mock.test.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { - describe, - it, - afterEach, - beforeEach, - mock - } from 'node:test' - import assert from 'node:assert' - - -// original value is 1 -const result = 1; - -class MyService { - static async myFunction() { - return Promise.resolve(result); - } -} - -describe('boilerplate with mock', () => { - beforeEach(() =>{ - // Setup required before each test - mock.restoreAll() - }) - afterEach(() => { - // Cleanup required after each test - }); - - it('should if ', async () => { - - // Replace the original implementation with a mock, returning 2 - const m = mock.method(MyService, "myFunction").mock; - m.mockImplementation(async () => Promise.resolve(2)) - - - // Test the function, but get the mocked value - const result = await MyService.myFunction(); - assert.strictEqual(result, - 2 - ) - }) - }) \ No newline at end of file diff --git a/testrunner-typescript/src/test-boilerplate/boilerplate.test.ts b/testrunner-typescript/src/test-boilerplate/boilerplate.test.ts deleted file mode 100644 index ad6e054d..00000000 --- a/testrunner-typescript/src/test-boilerplate/boilerplate.test.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { - describe, - it, - afterEach, - beforeEach, - mock - } from 'node:test' - import assert from 'node:assert' - -describe('boilerplate', () => { - beforeEach(() =>{ - // Setup required before each test - }) - afterEach(() => { - // Cleanup required after each test - }); - - it('should if ', async () => { - // Arrange - // - set up the test data and the expected result - // Act - // - call the function to test - // Assert - // - check the state: result returned from function - // - check the behavior: dependency function calls - - }) - }) \ No newline at end of file diff --git a/testrunner-typescript/src/test-fakes/fake-in-mem-db.test.ts b/testrunner-typescript/src/test-fakes/fake-in-mem-db.test.ts deleted file mode 100644 index 103000d6..00000000 --- a/testrunner-typescript/src/test-fakes/fake-in-mem-db.test.ts +++ /dev/null @@ -1,74 +0,0 @@ -// fake-in-mem-db.spec.ts -import { describe, it, beforeEach, afterEach } from 'node:test'; -import assert from 'node:assert'; - -class FakeDatabase { - private data: Record; - - constructor() { - this.data = {}; - } - - save(key: string, value: any): void { - this.data[key] = value; - } - - get(key: string): any { - return this.data[key]; - } -} - -// Function to test -function someTestFunction(db: FakeDatabase, key: string, value: any): any { - db.save(key, value); - return db.get(key); -} - -describe('In-Mem DB', () => { - let fakeDb: FakeDatabase; - let testKey: string; - let testValue: any; - let originalSave: (key: string, value: any) => void; - let saveSpyCallCount: number; - let saveSpyArgs: Array<[string, any]>; - - beforeEach(() => { - fakeDb = new FakeDatabase(); - testKey = 'testKey'; - testValue = { - first: 'John', - last: 'Jones', - lastUpdated: new Date().toISOString(), - }; - - // Create a simple spy on the save method - originalSave = fakeDb.save.bind(fakeDb); - saveSpyCallCount = 0; - saveSpyArgs = []; - fakeDb.save = function (key: string, value: any): void { - saveSpyCallCount++; - saveSpyArgs.push([key, value]); - return originalSave(key, value); - }; - }); - - afterEach(() => { - // Restore original method if necessary - fakeDb.save = originalSave; - }); - - it('should save and return the correct value', () => { - // Call the function under test - const result = someTestFunction(fakeDb, testKey, testValue); - - // Verify state - assert.deepStrictEqual(result, testValue); - assert.strictEqual(result.first, 'John'); - assert.strictEqual(result.last, 'Jones'); - assert.strictEqual(result.lastUpdated, testValue.lastUpdated); - - // Verify behavior using our manual spy - assert.strictEqual(saveSpyCallCount, 1); - assert.deepStrictEqual(saveSpyArgs[0], [testKey, testValue]); - }); -}); \ No newline at end of file diff --git a/testrunner-typescript/tsconfig.json b/testrunner-typescript/tsconfig.json deleted file mode 100644 index 4aeff363..00000000 --- a/testrunner-typescript/tsconfig.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "compilerOptions": { - "target": "es6", // Specify ECMAScript target version - "module": "commonjs", // Specify module code generation - "strict": true, // Enable all strict type-checking options - "esModuleInterop": true, // Enables emit interoperability between CommonJS and ES Modules - "noImplicitAny": false, // Enable error reporting for expressions and declarations with an implied 'any' type - "forceConsistentCasingInFileNames": true, // Disallow inconsistently-cased references to the same file - "outDir": "./dist", - "rootDir": "./", - "erasableSyntaxOnly": true, - }, - "include": [ - "src", - "test" - ], - "exclude": [ - "node_modules" - ] - } \ No newline at end of file diff --git a/testrunner/README.md b/testrunner/README.md deleted file mode 100644 index 376bbee7..00000000 --- a/testrunner/README.md +++ /dev/null @@ -1,29 +0,0 @@ -# Unit testing for the Azure SDK for JavaScript - -This subfolder is the source code for the [TBD article](). The purpose is to demonstrate unit test mocks for the Azure SDK for JavaScript. These specific examples use Azure Cosmos DB. - -## To run the test - -1. `npm install` -2. `npm run build` -3. `npm test` - - ```console - > unit-testing@1.0.0 test - > jest dist - - PASS dist/fakes/fake-in-mem-db.spec.js - PASS dist/mock-function/lib/insert.spec.js - - Test Suites: 2 passed, 2 total - Tests: 4 passed, 4 total - Snapshots: 0 total - Time: 4.247 s, estimated 5 s - Ran all test suites matching /dist/i. - ``` - -## Related content - -* [Passwordless connections for Azure services](https://learn.microsoft.com/azure/developer/intro/passwordless-overview) -* [Cosmos DB keyless access to the service](https://learn.microsoft.com/azure/cosmos-db/role-based-access-control) -* [Node.js Test Runner](https://nodejs.org/docs/latest/api/test.html#test-runner) \ No newline at end of file diff --git a/testrunner/package-lock.json b/testrunner/package-lock.json deleted file mode 100644 index 613ce82c..00000000 --- a/testrunner/package-lock.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "name": "testrunner", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "testrunner", - "version": "1.0.0", - "dependencies": { - "@types/node": "^22.13.10" - } - }, - "node_modules/@types/node": { - "version": "22.13.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.10.tgz", - "integrity": "sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==", - "license": "MIT", - "dependencies": { - "undici-types": "~6.20.0" - } - }, - "node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "license": "MIT" - } - } -} diff --git a/testrunner/package.json b/testrunner/package.json deleted file mode 100644 index b453d91b..00000000 --- a/testrunner/package.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "name": "testrunner", - "version": "1.0.0", - "main": "index.js", - "scripts": { - "build": "rm -rf dist && tsc", - "test": "npm run build && node --test --experimental-test-coverage --experimental-test-module-mocks --trace-exit", - "test:dev": "npm run build && node --watch --test test/ " - }, - "directories": { - "test": "test" - }, - "dependencies": { - "@types/node": "^22.13.10" - } -} diff --git a/testrunner/src/basics/01-spies.test.ts b/testrunner/src/basics/01-spies.test.ts deleted file mode 100644 index 8bcbea13..00000000 --- a/testrunner/src/basics/01-spies.test.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { - describe, - it, - mock -} from 'node:test'; -import assert from 'node:assert'; - - -function run({fn, times}){ - for(let i = 0; i < times; i++){ - fn({current: i * 5}); - } -} - -describe('Spies', () => { - it('should verify calls in a mock', () => { - const spy = mock.fn(); - run({fn: spy, times: 3}); - - assert.strictEqual(spy.mock.callCount(), 3); - const calls = spy.mock.calls; - - assert.deepStrictEqual(calls[0].arguments[0], {current: 0}); - assert.deepStrictEqual(calls[1].arguments[0], {current: 5}); - assert.deepStrictEqual(calls[2].arguments[0], {current: 10}); - - }); -}); \ No newline at end of file diff --git a/testrunner/src/basics/02-stubs.test.ts b/testrunner/src/basics/02-stubs.test.ts deleted file mode 100644 index 207abfe4..00000000 --- a/testrunner/src/basics/02-stubs.test.ts +++ /dev/null @@ -1,119 +0,0 @@ -import { - describe, - it, - beforeEach, - mock -} from 'node:test' -import assert from 'node:assert' - -class Service { - static async getTalks({ skip, limit }) { - const items = await fetch('https://tml-api.herokuapp.com/graphql', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - query: ` - { - getTalks (skip: ${skip}, limit: ${limit}) { - totalCount, - talks { - _id - title - } - } - } - ` - }) - }) - return (await items.json()).data.getTalks.talks - } -} - -function mapResponse(data) { - return data - .map(({ _id, title }, index) => `[${index}] id: ${_id}, title: ${title}`) - .join('\n') -} - -async function run({ skip = 0, limit = 10 }) { - const talks = mapResponse(await Service.getTalks({ skip, limit })) - return talks -} - -describe('Stub Test Suite', () => { - beforeEach(() => mock.restoreAll()) - - it('should stub APIs', async () => { - const m = mock.method( - Service, - "getTalks", - ).mock; - m.mockImplementation(async () => [ - { - _id: '63865750c839dbaacd8116e1', - title: 'The Journey About How I Fixed a Bug in the Node.js Core That Affected Thousands of Packages' - } - ]) - - const result = await run({ limit: 1 }) - const expected = `[0] id: 63865750c839dbaacd8116e1, title: The Journey About How I Fixed a Bug in the Node.js Core That Affected Thousands of Packages` - - assert.deepStrictEqual(m.callCount(), 1) - const calls = m.calls - - assert.deepStrictEqual(calls[0].arguments[0], { skip: 0, limit: 1 }) - assert.strictEqual(result, expected) - }) - - it('should stub different values for API calls', async () => { - const m = mock.method( - Service, - "getTalks", - ).mock - - m.mockImplementationOnce(async () => [ - { - _id: '63865750c839dbaacd8116e1', - title: 'The Journey About How I Fixed a Bug in the Node.js Core That Affected Thousands of Packages' - } - ], 0) - - m.mockImplementationOnce(async () => [ - { - _id: '01', - title: 'Mock 01' - } - ], 1) - - m.mockImplementationOnce(async () => [ - { - _id: '02', - title: 'Mock 02' - } - ], 2) - - { - const result = await run({ skip: 0, limit: 1 }) - const expected = `[0] id: 63865750c839dbaacd8116e1, title: The Journey About How I Fixed a Bug in the Node.js Core That Affected Thousands of Packages` - assert.strictEqual(result, expected) - } - { - const result = await run({ skip: 1, limit: 1 }) - const expected = `[0] id: 01, title: Mock 01` - assert.strictEqual(result, expected) - } - { - const result = await run({ skip: 2, limit: 1 }) - const expected = `[0] id: 02, title: Mock 02` - assert.strictEqual(result, expected) - } - - const calls = m.calls - assert.strictEqual(m.callCount(), 3) - assert.deepStrictEqual(calls[0].arguments[0], { skip: 0, limit: 1 }) - assert.deepStrictEqual(calls[1].arguments[0], { skip: 1, limit: 1 }) - assert.deepStrictEqual(calls[2].arguments[0], { skip: 2, limit: 1 }) - }) -}) \ No newline at end of file diff --git a/testrunner/src/fakes/fake-in-mem-db.test.ts b/testrunner/src/fakes/fake-in-mem-db.test.ts deleted file mode 100644 index 103000d6..00000000 --- a/testrunner/src/fakes/fake-in-mem-db.test.ts +++ /dev/null @@ -1,74 +0,0 @@ -// fake-in-mem-db.spec.ts -import { describe, it, beforeEach, afterEach } from 'node:test'; -import assert from 'node:assert'; - -class FakeDatabase { - private data: Record; - - constructor() { - this.data = {}; - } - - save(key: string, value: any): void { - this.data[key] = value; - } - - get(key: string): any { - return this.data[key]; - } -} - -// Function to test -function someTestFunction(db: FakeDatabase, key: string, value: any): any { - db.save(key, value); - return db.get(key); -} - -describe('In-Mem DB', () => { - let fakeDb: FakeDatabase; - let testKey: string; - let testValue: any; - let originalSave: (key: string, value: any) => void; - let saveSpyCallCount: number; - let saveSpyArgs: Array<[string, any]>; - - beforeEach(() => { - fakeDb = new FakeDatabase(); - testKey = 'testKey'; - testValue = { - first: 'John', - last: 'Jones', - lastUpdated: new Date().toISOString(), - }; - - // Create a simple spy on the save method - originalSave = fakeDb.save.bind(fakeDb); - saveSpyCallCount = 0; - saveSpyArgs = []; - fakeDb.save = function (key: string, value: any): void { - saveSpyCallCount++; - saveSpyArgs.push([key, value]); - return originalSave(key, value); - }; - }); - - afterEach(() => { - // Restore original method if necessary - fakeDb.save = originalSave; - }); - - it('should save and return the correct value', () => { - // Call the function under test - const result = someTestFunction(fakeDb, testKey, testValue); - - // Verify state - assert.deepStrictEqual(result, testValue); - assert.strictEqual(result.first, 'John'); - assert.strictEqual(result.last, 'Jones'); - assert.strictEqual(result.lastUpdated, testValue.lastUpdated); - - // Verify behavior using our manual spy - assert.strictEqual(saveSpyCallCount, 1); - assert.deepStrictEqual(saveSpyArgs[0], [testKey, testValue]); - }); -}); \ No newline at end of file diff --git a/testrunner/src/mock-function/lib/insert.test.ts b/testrunner/src/mock-function/lib/insert.test.ts deleted file mode 100644 index 69435df7..00000000 --- a/testrunner/src/mock-function/lib/insert.test.ts +++ /dev/null @@ -1,137 +0,0 @@ -// insertDocument.test.ts -import { describe, it, beforeEach, mock } from 'node:test'; -import assert from 'node:assert'; -import { Container } from '../data/connect-to-cosmos'; -import { createTestInputAndResult } from '../data/fake-data'; -import type { - DbDocument, - DbError, - RawInput, -} from '../data/model'; -import { - isDbError, - isVerificationErrors, -} from '../data/model'; -// Instead of jest.mock, import the whole module to override functions as needed. -import * as verifyModule from '../data/verify'; -import * as dbModule from '../data/connect-to-cosmos'; -import { insertDocument } from './insert'; - -// --- Test suite for insertDocument --- -describe('Insert into db', () => { - - // Set up a fresh container object before each test. - beforeEach(() => { - // Setup required before each test - mock.restoreAll() - }) - - it('should insert document successfully', async () => { - // Arrange: override inputVerified to return true. - const { input, result }: { input: RawInput; result: Partial } = createTestInputAndResult(); - - const fakeContainer = { - items: { - create: async (doc: any) => { - return { resource: result }; - }, - }, - } as unknown as Container; - - const mVerify = mock.method(verifyModule, "inputVerified").mock; - mVerify.mockImplementation(() => true); - - const mContainerCreate = mock.method(fakeContainer.items as any, "create").mock; - mContainerCreate.mockImplementation(async (doc: any) => { - return { resource: result }; - }); - - // Act: - const receivedResult = await insertDocument(fakeContainer, input); - - // Assert - State verification: Ensure the result is as expected. - assert.deepStrictEqual(receivedResult, result); - - // Assert - Behavior verification: Ensure create was called once with correct arguments. - assert.strictEqual(mContainerCreate.callCount(), 1); - assert.deepStrictEqual(mContainerCreate.calls[0].arguments[0], { - id: input.id, - name: result.name, - }); - }); -}); - - -it('should return verification error if input is not verified', async () => { - - const fakeContainer = { - items: { - create: async (_: any) => { - throw new Error('Create method not implemented'); - }, - }, - } as unknown as Container; - - const mVerify = mock.method(verifyModule, "inputVerified").mock; - mVerify.mockImplementation(() => false); - - const mGetUniqueId = mock.method(dbModule, "getUniqueId").mock; - mGetUniqueId.mockImplementation(() => 'unique-id'); - - const mContainerCreate = mock.method(fakeContainer.items, "create").mock; - - // Arrange: wrong shape of document on purpose. - const doc = { name: 'test' } as unknown as RawInput; - - // Act: - const insertDocumentResult = await insertDocument(fakeContainer, doc); - - // Assert - State verification. - if (isVerificationErrors(insertDocumentResult)) { - assert.deepStrictEqual(insertDocumentResult, { message: 'Verification failed' }); - } else { - throw new Error('Result is not of type VerificationErrors'); - } - - // Assert - Behavior verification: Verify that create was never called. - assert.strictEqual(mContainerCreate.callCount(), 0); - -}); - - -it('should return error if db insert fails', async () => { - // Arrange: override inputVerified to return true. - const { input, result } = createTestInputAndResult(); - let errorMessage: string = 'An unknown error occurred'; - - const fakeContainer = { - items: { - create: async (doc: any): Promise => { - return Promise.resolve(null); - }, - }, - } as unknown as Container; - - const mVerify = mock.method(verifyModule, "inputVerified").mock; - mVerify.mockImplementation(() => true); - - const mContainerCreate = mock.method(fakeContainer.items, "create").mock; - mContainerCreate.mockImplementation = async (doc: any) => { - const mockError: DbError = { - message: errorMessage, - code: 500, - }; - throw mockError; - } - - // Act: - const insertDocumentResult = await insertDocument(fakeContainer, input); - - // // Assert - Ensure create method was called once with the correct arguments. - assert.strictEqual(isDbError(insertDocumentResult), true); - assert.strictEqual(mContainerCreate.callCount(), 1); - assert.deepStrictEqual(mContainerCreate.calls[0].arguments[0], { - id: input.id, - name: result.name, - }); -}); diff --git a/testrunner/src/test-boilerplate/boilerplate-with-mock.test-2.ts b/testrunner/src/test-boilerplate/boilerplate-with-mock.test-2.ts deleted file mode 100644 index ded260d2..00000000 --- a/testrunner/src/test-boilerplate/boilerplate-with-mock.test-2.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { - describe, - it, - afterEach, - beforeEach, - mock - } from 'node:test' -import assert from 'node:assert'; - -import * as MyService from '../mock-function/data/connect-to-cosmos' - -describe('boilerplate with mock 2', () => { - beforeEach(() =>{ - // Setup required before each test - mock.restoreAll() - }) - afterEach(() => { - // Cleanup required after each test - }); - - it('should if ', async () => { - - const m = mock.method(MyService, "getUniqueId"); - m.mock.mockImplementation(() => { - // Replace the original implementation with a mock result - - // return fake guid - return '12345678-1234-1234-1234-123456789012' - - }); - - const result = await MyService.getUniqueId(); - assert.strictEqual(result, - '12345678-1234-1234-1234-123456789012' - ) - - }) - }) \ No newline at end of file diff --git a/testrunner/src/test-boilerplate/boilerplate-with-mock.test.ts b/testrunner/src/test-boilerplate/boilerplate-with-mock.test.ts deleted file mode 100644 index da8e0f5a..00000000 --- a/testrunner/src/test-boilerplate/boilerplate-with-mock.test.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { - describe, - it, - afterEach, - beforeEach, - mock - } from 'node:test' - import assert from 'node:assert' - - -// original value is 1 -const result = 1; - -class MyService { - static async myFunction() { - return Promise.resolve(result); - } -} - -describe('boilerplate with mock', () => { - beforeEach(() =>{ - // Setup required before each test - mock.restoreAll() - }) - afterEach(() => { - // Cleanup required after each test - }); - - it('should if ', async () => { - - // Replace the original implementation with a mock, returning 2 - const m = mock.method(MyService, "myFunction").mock; - m.mockImplementation(async () => Promise.resolve(2)) - - - // Test the function, but get the mocked value - const result = await MyService.myFunction(); - assert.strictEqual(result, - 2 - ) - }) - }) \ No newline at end of file diff --git a/testrunner/src/test-boilerplate/boilerplate.test.ts b/testrunner/src/test-boilerplate/boilerplate.test.ts deleted file mode 100644 index ad6e054d..00000000 --- a/testrunner/src/test-boilerplate/boilerplate.test.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { - describe, - it, - afterEach, - beforeEach, - mock - } from 'node:test' - import assert from 'node:assert' - -describe('boilerplate', () => { - beforeEach(() =>{ - // Setup required before each test - }) - afterEach(() => { - // Cleanup required after each test - }); - - it('should if ', async () => { - // Arrange - // - set up the test data and the expected result - // Act - // - call the function to test - // Assert - // - check the state: result returned from function - // - check the behavior: dependency function calls - - }) - }) \ No newline at end of file diff --git a/unit-testing/package.json b/unit-testing/package.json index ab9a874e..d3e876ff 100644 --- a/unit-testing/package.json +++ b/unit-testing/package.json @@ -6,8 +6,8 @@ "scripts": { "clear": "rm -rf dist && rm -rf coverage", "start": "node dist/mock-function/index.js", - "test": "jest dist", - "test:coverage": "jest dist --coverage", + "test": "jest --detectOpenHandles dist", + "test:coverage": "jest --detectOpenHandles dist --coverage", "build": "npm run clear && tsc", "lint": "eslint \"./src/**/*.ts\" --fix", "format": "prettier \"./src/**/*.ts\" --write", diff --git a/unit-testing/src/mock-function/lib/insert.spec.ts b/unit-testing/src/mock-function/lib/insert.spec.ts index 659c37a4..a7d206d7 100644 --- a/unit-testing/src/mock-function/lib/insert.spec.ts +++ b/unit-testing/src/mock-function/lib/insert.spec.ts @@ -22,7 +22,7 @@ jest.mock('../data/verify', () => ({ inputVerified: jest.fn(), })); -describe('insertDocument', () => { +describe('SDK', () => { // Mock the Cosmo DB Container object let mockContainer: jest.Mocked; diff --git a/unit-testing/tsconfig.json b/unit-testing/tsconfig.json index 237b8c78..9f817d53 100644 --- a/unit-testing/tsconfig.json +++ b/unit-testing/tsconfig.json @@ -6,7 +6,7 @@ "esModuleInterop": true, // Enables emit interoperability between CommonJS and ES Modules "forceConsistentCasingInFileNames": true, // Disallow inconsistently-cased references to the same file "types": ["jest", "node"], // Specify type package - "outDir": "./dist", - "rootDir": "./src", + "outDir": "dist", + "rootDir": "src", }, } \ No newline at end of file From 022feae02ab708be52b06a2ba7d752ddb2f84344 Mon Sep 17 00:00:00 2001 From: Dina Berry Date: Fri, 14 Mar 2025 21:43:23 +0000 Subject: [PATCH 16/27] one insert test passes --- .../src/mock-function/lib/insert.spec.ts | 6 +- .../coverage/lcov-report/base.css | 224 ------------------ .../coverage/lcov-report/block-navigation.js | 87 ------- .../coverage/lcov-report/favicon.png | Bin 445 -> 0 bytes .../coverage/lcov-report/index.html | 146 ------------ .../coverage/lcov-report/prettify.css | 1 - .../coverage/lcov-report/prettify.js | 2 - .../lcov-report/sort-arrow-sprite.png | Bin 138 -> 0 bytes .../coverage/lcov-report/sorter.js | 196 --------------- .../src/data/connect-to-cosmos.ts.html | 202 ---------------- .../lcov-report/src/data/fake-data.ts.html | 169 ------------- .../coverage/lcov-report/src/data/index.html | 161 ------------- .../lcov-report/src/data/model.ts.html | 202 ---------------- .../lcov-report/src/data/verify.ts.html | 112 --------- .../coverage/lcov-report/src/index.html | 116 --------- .../coverage/lcov-report/src/index.ts.html | 136 ----------- .../coverage/lcov-report/src/lib/index.html | 116 --------- .../lcov-report/src/lib/insert.ts.html | 208 ---------------- test-with-vitest/tests/boilerplate.test.ts | 3 +- test-with-vitest/tests/insert.test.ts | 139 ++++------- test-with-vitest/tests/insert.test.ts.old | 132 +++++++++++ 21 files changed, 188 insertions(+), 2170 deletions(-) delete mode 100644 test-with-vitest/coverage/lcov-report/base.css delete mode 100644 test-with-vitest/coverage/lcov-report/block-navigation.js delete mode 100644 test-with-vitest/coverage/lcov-report/favicon.png delete mode 100644 test-with-vitest/coverage/lcov-report/index.html delete mode 100644 test-with-vitest/coverage/lcov-report/prettify.css delete mode 100644 test-with-vitest/coverage/lcov-report/prettify.js delete mode 100644 test-with-vitest/coverage/lcov-report/sort-arrow-sprite.png delete mode 100644 test-with-vitest/coverage/lcov-report/sorter.js delete mode 100644 test-with-vitest/coverage/lcov-report/src/data/connect-to-cosmos.ts.html delete mode 100644 test-with-vitest/coverage/lcov-report/src/data/fake-data.ts.html delete mode 100644 test-with-vitest/coverage/lcov-report/src/data/index.html delete mode 100644 test-with-vitest/coverage/lcov-report/src/data/model.ts.html delete mode 100644 test-with-vitest/coverage/lcov-report/src/data/verify.ts.html delete mode 100644 test-with-vitest/coverage/lcov-report/src/index.html delete mode 100644 test-with-vitest/coverage/lcov-report/src/index.ts.html delete mode 100644 test-with-vitest/coverage/lcov-report/src/lib/index.html delete mode 100644 test-with-vitest/coverage/lcov-report/src/lib/insert.ts.html create mode 100644 test-with-vitest/tests/insert.test.ts.old diff --git a/test-with-jest/src/mock-function/lib/insert.spec.ts b/test-with-jest/src/mock-function/lib/insert.spec.ts index a7d206d7..5c5bf3e7 100644 --- a/test-with-jest/src/mock-function/lib/insert.spec.ts +++ b/test-with-jest/src/mock-function/lib/insert.spec.ts @@ -1,12 +1,14 @@ // insertDocument.test.ts import { Container } from '../data/connect-to-cosmos'; import { createTestInputAndResult } from '../data/fake-data'; -import { +import type { DbDocument, DbError, + RawInput +} from '../data/model'; +import { isDbError, isVerificationErrors, - RawInput, } from '../data/model'; import { inputVerified } from '../data/verify'; import { insertDocument } from './insert'; diff --git a/test-with-vitest/coverage/lcov-report/base.css b/test-with-vitest/coverage/lcov-report/base.css deleted file mode 100644 index f418035b..00000000 --- a/test-with-vitest/coverage/lcov-report/base.css +++ /dev/null @@ -1,224 +0,0 @@ -body, html { - margin:0; padding: 0; - height: 100%; -} -body { - font-family: Helvetica Neue, Helvetica, Arial; - font-size: 14px; - color:#333; -} -.small { font-size: 12px; } -*, *:after, *:before { - -webkit-box-sizing:border-box; - -moz-box-sizing:border-box; - box-sizing:border-box; - } -h1 { font-size: 20px; margin: 0;} -h2 { font-size: 14px; } -pre { - font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace; - margin: 0; - padding: 0; - -moz-tab-size: 2; - -o-tab-size: 2; - tab-size: 2; -} -a { color:#0074D9; text-decoration:none; } -a:hover { text-decoration:underline; } -.strong { font-weight: bold; } -.space-top1 { padding: 10px 0 0 0; } -.pad2y { padding: 20px 0; } -.pad1y { padding: 10px 0; } -.pad2x { padding: 0 20px; } -.pad2 { padding: 20px; } -.pad1 { padding: 10px; } -.space-left2 { padding-left:55px; } -.space-right2 { padding-right:20px; } -.center { text-align:center; } -.clearfix { display:block; } -.clearfix:after { - content:''; - display:block; - height:0; - clear:both; - visibility:hidden; - } -.fl { float: left; } -@media only screen and (max-width:640px) { - .col3 { width:100%; max-width:100%; } - .hide-mobile { display:none!important; } -} - -.quiet { - color: #7f7f7f; - color: rgba(0,0,0,0.5); -} -.quiet a { opacity: 0.7; } - -.fraction { - font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; - font-size: 10px; - color: #555; - background: #E8E8E8; - padding: 4px 5px; - border-radius: 3px; - vertical-align: middle; -} - -div.path a:link, div.path a:visited { color: #333; } -table.coverage { - border-collapse: collapse; - margin: 10px 0 0 0; - padding: 0; -} - -table.coverage td { - margin: 0; - padding: 0; - vertical-align: top; -} -table.coverage td.line-count { - text-align: right; - padding: 0 5px 0 20px; -} -table.coverage td.line-coverage { - text-align: right; - padding-right: 10px; - min-width:20px; -} - -table.coverage td span.cline-any { - display: inline-block; - padding: 0 5px; - width: 100%; -} -.missing-if-branch { - display: inline-block; - margin-right: 5px; - border-radius: 3px; - position: relative; - padding: 0 4px; - background: #333; - color: yellow; -} - -.skip-if-branch { - display: none; - margin-right: 10px; - position: relative; - padding: 0 4px; - background: #ccc; - color: white; -} -.missing-if-branch .typ, .skip-if-branch .typ { - color: inherit !important; -} -.coverage-summary { - border-collapse: collapse; - width: 100%; -} -.coverage-summary tr { border-bottom: 1px solid #bbb; } -.keyline-all { border: 1px solid #ddd; } -.coverage-summary td, .coverage-summary th { padding: 10px; } -.coverage-summary tbody { border: 1px solid #bbb; } -.coverage-summary td { border-right: 1px solid #bbb; } -.coverage-summary td:last-child { border-right: none; } -.coverage-summary th { - text-align: left; - font-weight: normal; - white-space: nowrap; -} -.coverage-summary th.file { border-right: none !important; } -.coverage-summary th.pct { } -.coverage-summary th.pic, -.coverage-summary th.abs, -.coverage-summary td.pct, -.coverage-summary td.abs { text-align: right; } -.coverage-summary td.file { white-space: nowrap; } -.coverage-summary td.pic { min-width: 120px !important; } -.coverage-summary tfoot td { } - -.coverage-summary .sorter { - height: 10px; - width: 7px; - display: inline-block; - margin-left: 0.5em; - background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent; -} -.coverage-summary .sorted .sorter { - background-position: 0 -20px; -} -.coverage-summary .sorted-desc .sorter { - background-position: 0 -10px; -} -.status-line { height: 10px; } -/* yellow */ -.cbranch-no { background: yellow !important; color: #111; } -/* dark red */ -.red.solid, .status-line.low, .low .cover-fill { background:#C21F39 } -.low .chart { border:1px solid #C21F39 } -.highlighted, -.highlighted .cstat-no, .highlighted .fstat-no, .highlighted .cbranch-no{ - background: #C21F39 !important; -} -/* medium red */ -.cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE } -/* light red */ -.low, .cline-no { background:#FCE1E5 } -/* light green */ -.high, .cline-yes { background:rgb(230,245,208) } -/* medium green */ -.cstat-yes { background:rgb(161,215,106) } -/* dark green */ -.status-line.high, .high .cover-fill { background:rgb(77,146,33) } -.high .chart { border:1px solid rgb(77,146,33) } -/* dark yellow (gold) */ -.status-line.medium, .medium .cover-fill { background: #f9cd0b; } -.medium .chart { border:1px solid #f9cd0b; } -/* light yellow */ -.medium { background: #fff4c2; } - -.cstat-skip { background: #ddd; color: #111; } -.fstat-skip { background: #ddd; color: #111 !important; } -.cbranch-skip { background: #ddd !important; color: #111; } - -span.cline-neutral { background: #eaeaea; } - -.coverage-summary td.empty { - opacity: .5; - padding-top: 4px; - padding-bottom: 4px; - line-height: 1; - color: #888; -} - -.cover-fill, .cover-empty { - display:inline-block; - height: 12px; -} -.chart { - line-height: 0; -} -.cover-empty { - background: white; -} -.cover-full { - border-right: none !important; -} -pre.prettyprint { - border: none !important; - padding: 0 !important; - margin: 0 !important; -} -.com { color: #999 !important; } -.ignore-none { color: #999; font-weight: normal; } - -.wrapper { - min-height: 100%; - height: auto !important; - height: 100%; - margin: 0 auto -48px; -} -.footer, .push { - height: 48px; -} diff --git a/test-with-vitest/coverage/lcov-report/block-navigation.js b/test-with-vitest/coverage/lcov-report/block-navigation.js deleted file mode 100644 index cc121302..00000000 --- a/test-with-vitest/coverage/lcov-report/block-navigation.js +++ /dev/null @@ -1,87 +0,0 @@ -/* eslint-disable */ -var jumpToCode = (function init() { - // Classes of code we would like to highlight in the file view - var missingCoverageClasses = ['.cbranch-no', '.cstat-no', '.fstat-no']; - - // Elements to highlight in the file listing view - var fileListingElements = ['td.pct.low']; - - // We don't want to select elements that are direct descendants of another match - var notSelector = ':not(' + missingCoverageClasses.join('):not(') + ') > '; // becomes `:not(a):not(b) > ` - - // Selecter that finds elements on the page to which we can jump - var selector = - fileListingElements.join(', ') + - ', ' + - notSelector + - missingCoverageClasses.join(', ' + notSelector); // becomes `:not(a):not(b) > a, :not(a):not(b) > b` - - // The NodeList of matching elements - var missingCoverageElements = document.querySelectorAll(selector); - - var currentIndex; - - function toggleClass(index) { - missingCoverageElements - .item(currentIndex) - .classList.remove('highlighted'); - missingCoverageElements.item(index).classList.add('highlighted'); - } - - function makeCurrent(index) { - toggleClass(index); - currentIndex = index; - missingCoverageElements.item(index).scrollIntoView({ - behavior: 'smooth', - block: 'center', - inline: 'center' - }); - } - - function goToPrevious() { - var nextIndex = 0; - if (typeof currentIndex !== 'number' || currentIndex === 0) { - nextIndex = missingCoverageElements.length - 1; - } else if (missingCoverageElements.length > 1) { - nextIndex = currentIndex - 1; - } - - makeCurrent(nextIndex); - } - - function goToNext() { - var nextIndex = 0; - - if ( - typeof currentIndex === 'number' && - currentIndex < missingCoverageElements.length - 1 - ) { - nextIndex = currentIndex + 1; - } - - makeCurrent(nextIndex); - } - - return function jump(event) { - if ( - document.getElementById('fileSearch') === document.activeElement && - document.activeElement != null - ) { - // if we're currently focused on the search input, we don't want to navigate - return; - } - - switch (event.which) { - case 78: // n - case 74: // j - goToNext(); - break; - case 66: // b - case 75: // k - case 80: // p - goToPrevious(); - break; - } - }; -})(); -window.addEventListener('keydown', jumpToCode); diff --git a/test-with-vitest/coverage/lcov-report/favicon.png b/test-with-vitest/coverage/lcov-report/favicon.png deleted file mode 100644 index c1525b811a167671e9de1fa78aab9f5c0b61cef7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 445 zcmV;u0Yd(XP))rP{nL}Ln%S7`m{0DjX9TLF* zFCb$4Oi7vyLOydb!7n&^ItCzb-%BoB`=x@N2jll2Nj`kauio%aw_@fe&*}LqlFT43 z8doAAe))z_%=P%v^@JHp3Hjhj^6*Kr_h|g_Gr?ZAa&y>wxHE99Gk>A)2MplWz2xdG zy8VD2J|Uf#EAw*bo5O*PO_}X2Tob{%bUoO2G~T`@%S6qPyc}VkhV}UifBuRk>%5v( z)x7B{I~z*k<7dv#5tC+m{km(D087J4O%+<<;K|qwefb6@GSX45wCK}Sn*> - - - - Code coverage report for All files - - - - - - - - - -
-
-

All files

-
- -
- 36.95% - Statements - 17/46 -
- - -
- 31.25% - Branches - 5/16 -
- - -
- 42.85% - Functions - 6/14 -
- - -
- 36.95% - Lines - 17/46 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FileStatementsBranchesFunctionsLines
src -
-
0%0/7100%0/00%0/30%0/7
src/data -
-
32.14%9/2820%2/1050%5/1032.14%9/28
src/lib -
-
72.72%8/1150%3/6100%1/172.72%8/11
-
-
-
- - - - - - - - \ No newline at end of file diff --git a/test-with-vitest/coverage/lcov-report/prettify.css b/test-with-vitest/coverage/lcov-report/prettify.css deleted file mode 100644 index b317a7cd..00000000 --- a/test-with-vitest/coverage/lcov-report/prettify.css +++ /dev/null @@ -1 +0,0 @@ -.pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee} diff --git a/test-with-vitest/coverage/lcov-report/prettify.js b/test-with-vitest/coverage/lcov-report/prettify.js deleted file mode 100644 index b3225238..00000000 --- a/test-with-vitest/coverage/lcov-report/prettify.js +++ /dev/null @@ -1,2 +0,0 @@ -/* eslint-disable */ -window.PR_SHOULD_USE_CONTINUATION=true;(function(){var h=["break,continue,do,else,for,if,return,while"];var u=[h,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"];var p=[u,"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"];var l=[p,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"];var x=[p,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"];var R=[x,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"];var r="all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes";var w=[p,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"];var s="caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END";var I=[h,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"];var f=[h,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"];var H=[h,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"];var A=[l,R,w,s+I,f,H];var e=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/;var C="str";var z="kwd";var j="com";var O="typ";var G="lit";var L="pun";var F="pln";var m="tag";var E="dec";var J="src";var P="atn";var n="atv";var N="nocode";var M="(?:^^\\.?|[+-]|\\!|\\!=|\\!==|\\#|\\%|\\%=|&|&&|&&=|&=|\\(|\\*|\\*=|\\+=|\\,|\\-=|\\->|\\/|\\/=|:|::|\\;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\@|\\[|\\^|\\^=|\\^\\^|\\^\\^=|\\{|\\||\\|=|\\|\\||\\|\\|=|\\~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*";function k(Z){var ad=0;var S=false;var ac=false;for(var V=0,U=Z.length;V122)){if(!(al<65||ag>90)){af.push([Math.max(65,ag)|32,Math.min(al,90)|32])}if(!(al<97||ag>122)){af.push([Math.max(97,ag)&~32,Math.min(al,122)&~32])}}}}af.sort(function(av,au){return(av[0]-au[0])||(au[1]-av[1])});var ai=[];var ap=[NaN,NaN];for(var ar=0;arat[0]){if(at[1]+1>at[0]){an.push("-")}an.push(T(at[1]))}}an.push("]");return an.join("")}function W(al){var aj=al.source.match(new RegExp("(?:\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]|\\\\u[A-Fa-f0-9]{4}|\\\\x[A-Fa-f0-9]{2}|\\\\[0-9]+|\\\\[^ux0-9]|\\(\\?[:!=]|[\\(\\)\\^]|[^\\x5B\\x5C\\(\\)\\^]+)","g"));var ah=aj.length;var an=[];for(var ak=0,am=0;ak=2&&ai==="["){aj[ak]=X(ag)}else{if(ai!=="\\"){aj[ak]=ag.replace(/[a-zA-Z]/g,function(ao){var ap=ao.charCodeAt(0);return"["+String.fromCharCode(ap&~32,ap|32)+"]"})}}}}return aj.join("")}var aa=[];for(var V=0,U=Z.length;V=0;){S[ac.charAt(ae)]=Y}}var af=Y[1];var aa=""+af;if(!ag.hasOwnProperty(aa)){ah.push(af);ag[aa]=null}}ah.push(/[\0-\uffff]/);V=k(ah)})();var X=T.length;var W=function(ah){var Z=ah.sourceCode,Y=ah.basePos;var ad=[Y,F];var af=0;var an=Z.match(V)||[];var aj={};for(var ae=0,aq=an.length;ae=5&&"lang-"===ap.substring(0,5);if(am&&!(ai&&typeof ai[1]==="string")){am=false;ap=J}if(!am){aj[ag]=ap}}var ab=af;af+=ag.length;if(!am){ad.push(Y+ab,ap)}else{var al=ai[1];var ak=ag.indexOf(al);var ac=ak+al.length;if(ai[2]){ac=ag.length-ai[2].length;ak=ac-al.length}var ar=ap.substring(5);B(Y+ab,ag.substring(0,ak),W,ad);B(Y+ab+ak,al,q(ar,al),ad);B(Y+ab+ac,ag.substring(ac),W,ad)}}ah.decorations=ad};return W}function i(T){var W=[],S=[];if(T.tripleQuotedStrings){W.push([C,/^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,null,"'\""])}else{if(T.multiLineStrings){W.push([C,/^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,null,"'\"`"])}else{W.push([C,/^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,null,"\"'"])}}if(T.verbatimStrings){S.push([C,/^@\"(?:[^\"]|\"\")*(?:\"|$)/,null])}var Y=T.hashComments;if(Y){if(T.cStyleComments){if(Y>1){W.push([j,/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,null,"#"])}else{W.push([j,/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\r\n]*)/,null,"#"])}S.push([C,/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,null])}else{W.push([j,/^#[^\r\n]*/,null,"#"])}}if(T.cStyleComments){S.push([j,/^\/\/[^\r\n]*/,null]);S.push([j,/^\/\*[\s\S]*?(?:\*\/|$)/,null])}if(T.regexLiterals){var X=("/(?=[^/*])(?:[^/\\x5B\\x5C]|\\x5C[\\s\\S]|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+/");S.push(["lang-regex",new RegExp("^"+M+"("+X+")")])}var V=T.types;if(V){S.push([O,V])}var U=(""+T.keywords).replace(/^ | $/g,"");if(U.length){S.push([z,new RegExp("^(?:"+U.replace(/[\s,]+/g,"|")+")\\b"),null])}W.push([F,/^\s+/,null," \r\n\t\xA0"]);S.push([G,/^@[a-z_$][a-z_$@0-9]*/i,null],[O,/^(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\w+_t\b)/,null],[F,/^[a-z_$][a-z_$@0-9]*/i,null],[G,new RegExp("^(?:0x[a-f0-9]+|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)(?:e[+\\-]?\\d+)?)[a-z]*","i"),null,"0123456789"],[F,/^\\[\s\S]?/,null],[L,/^.[^\s\w\.$@\'\"\`\/\#\\]*/,null]);return g(W,S)}var K=i({keywords:A,hashComments:true,cStyleComments:true,multiLineStrings:true,regexLiterals:true});function Q(V,ag){var U=/(?:^|\s)nocode(?:\s|$)/;var ab=/\r\n?|\n/;var ac=V.ownerDocument;var S;if(V.currentStyle){S=V.currentStyle.whiteSpace}else{if(window.getComputedStyle){S=ac.defaultView.getComputedStyle(V,null).getPropertyValue("white-space")}}var Z=S&&"pre"===S.substring(0,3);var af=ac.createElement("LI");while(V.firstChild){af.appendChild(V.firstChild)}var W=[af];function ae(al){switch(al.nodeType){case 1:if(U.test(al.className)){break}if("BR"===al.nodeName){ad(al);if(al.parentNode){al.parentNode.removeChild(al)}}else{for(var an=al.firstChild;an;an=an.nextSibling){ae(an)}}break;case 3:case 4:if(Z){var am=al.nodeValue;var aj=am.match(ab);if(aj){var ai=am.substring(0,aj.index);al.nodeValue=ai;var ah=am.substring(aj.index+aj[0].length);if(ah){var ak=al.parentNode;ak.insertBefore(ac.createTextNode(ah),al.nextSibling)}ad(al);if(!ai){al.parentNode.removeChild(al)}}}break}}function ad(ak){while(!ak.nextSibling){ak=ak.parentNode;if(!ak){return}}function ai(al,ar){var aq=ar?al.cloneNode(false):al;var ao=al.parentNode;if(ao){var ap=ai(ao,1);var an=al.nextSibling;ap.appendChild(aq);for(var am=an;am;am=an){an=am.nextSibling;ap.appendChild(am)}}return aq}var ah=ai(ak.nextSibling,0);for(var aj;(aj=ah.parentNode)&&aj.nodeType===1;){ah=aj}W.push(ah)}for(var Y=0;Y=S){ah+=2}if(V>=ap){Z+=2}}}var t={};function c(U,V){for(var S=V.length;--S>=0;){var T=V[S];if(!t.hasOwnProperty(T)){t[T]=U}else{if(window.console){console.warn("cannot override language handler %s",T)}}}}function q(T,S){if(!(T&&t.hasOwnProperty(T))){T=/^\s*]*(?:>|$)/],[j,/^<\!--[\s\S]*?(?:-\->|$)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],[L,/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),["default-markup","htm","html","mxml","xhtml","xml","xsl"]);c(g([[F,/^[\s]+/,null," \t\r\n"],[n,/^(?:\"[^\"]*\"?|\'[^\']*\'?)/,null,"\"'"]],[[m,/^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],[P,/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],[L,/^[=<>\/]+/],["lang-js",/^on\w+\s*=\s*\"([^\"]+)\"/i],["lang-js",/^on\w+\s*=\s*\'([^\']+)\'/i],["lang-js",/^on\w+\s*=\s*([^\"\'>\s]+)/i],["lang-css",/^style\s*=\s*\"([^\"]+)\"/i],["lang-css",/^style\s*=\s*\'([^\']+)\'/i],["lang-css",/^style\s*=\s*([^\"\'>\s]+)/i]]),["in.tag"]);c(g([],[[n,/^[\s\S]+/]]),["uq.val"]);c(i({keywords:l,hashComments:true,cStyleComments:true,types:e}),["c","cc","cpp","cxx","cyc","m"]);c(i({keywords:"null,true,false"}),["json"]);c(i({keywords:R,hashComments:true,cStyleComments:true,verbatimStrings:true,types:e}),["cs"]);c(i({keywords:x,cStyleComments:true}),["java"]);c(i({keywords:H,hashComments:true,multiLineStrings:true}),["bsh","csh","sh"]);c(i({keywords:I,hashComments:true,multiLineStrings:true,tripleQuotedStrings:true}),["cv","py"]);c(i({keywords:s,hashComments:true,multiLineStrings:true,regexLiterals:true}),["perl","pl","pm"]);c(i({keywords:f,hashComments:true,multiLineStrings:true,regexLiterals:true}),["rb"]);c(i({keywords:w,cStyleComments:true,regexLiterals:true}),["js"]);c(i({keywords:r,hashComments:3,cStyleComments:true,multilineStrings:true,tripleQuotedStrings:true,regexLiterals:true}),["coffee"]);c(g([],[[C,/^[\s\S]+/]]),["regex"]);function d(V){var U=V.langExtension;try{var S=a(V.sourceNode);var T=S.sourceCode;V.sourceCode=T;V.spans=S.spans;V.basePos=0;q(U,T)(V);D(V)}catch(W){if("console" in window){console.log(W&&W.stack?W.stack:W)}}}function y(W,V,U){var S=document.createElement("PRE");S.innerHTML=W;if(U){Q(S,U)}var T={langExtension:V,numberLines:U,sourceNode:S};d(T);return S.innerHTML}function b(ad){function Y(af){return document.getElementsByTagName(af)}var ac=[Y("pre"),Y("code"),Y("xmp")];var T=[];for(var aa=0;aa=0){var ah=ai.match(ab);var am;if(!ah&&(am=o(aj))&&"CODE"===am.tagName){ah=am.className.match(ab)}if(ah){ah=ah[1]}var al=false;for(var ak=aj.parentNode;ak;ak=ak.parentNode){if((ak.tagName==="pre"||ak.tagName==="code"||ak.tagName==="xmp")&&ak.className&&ak.className.indexOf("prettyprint")>=0){al=true;break}}if(!al){var af=aj.className.match(/\blinenums\b(?::(\d+))?/);af=af?af[1]&&af[1].length?+af[1]:true:false;if(af){Q(aj,af)}S={langExtension:ah,sourceNode:aj,numberLines:af};d(S)}}}if(X]*(?:>|$)/],[PR.PR_COMMENT,/^<\!--[\s\S]*?(?:-\->|$)/],[PR.PR_PUNCTUATION,/^(?:<[%?]|[%?]>)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-handlebars",/^]*type\s*=\s*['"]?text\/x-handlebars-template['"]?\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i],[PR.PR_DECLARATION,/^{{[#^>/]?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{&?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{{>?\s*[\w.][^}]*}}}/],[PR.PR_COMMENT,/^{{![^}]*}}/]]),["handlebars","hbs"]);PR.registerLangHandler(PR.createSimpleLexer([[PR.PR_PLAIN,/^[ \t\r\n\f]+/,null," \t\r\n\f"]],[[PR.PR_STRING,/^\"(?:[^\n\r\f\\\"]|\\(?:\r\n?|\n|\f)|\\[\s\S])*\"/,null],[PR.PR_STRING,/^\'(?:[^\n\r\f\\\']|\\(?:\r\n?|\n|\f)|\\[\s\S])*\'/,null],["lang-css-str",/^url\(([^\)\"\']*)\)/i],[PR.PR_KEYWORD,/^(?:url|rgb|\!important|@import|@page|@media|@charset|inherit)(?=[^\-\w]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|(?:\\[0-9a-f]+ ?))(?:[_a-z0-9\-]|\\(?:\\[0-9a-f]+ ?))*)\s*:/i],[PR.PR_COMMENT,/^\/\*[^*]*\*+(?:[^\/*][^*]*\*+)*\//],[PR.PR_COMMENT,/^(?:)/],[PR.PR_LITERAL,/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],[PR.PR_LITERAL,/^#(?:[0-9a-f]{3}){1,2}/i],[PR.PR_PLAIN,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i],[PR.PR_PUNCTUATION,/^[^\s\w\'\"]+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_KEYWORD,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_STRING,/^[^\)\"\']+/]]),["css-str"]); diff --git a/test-with-vitest/coverage/lcov-report/sort-arrow-sprite.png b/test-with-vitest/coverage/lcov-report/sort-arrow-sprite.png deleted file mode 100644 index 6ed68316eb3f65dec9063332d2f69bf3093bbfab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 138 zcmeAS@N?(olHy`uVBq!ia0vp^>_9Bd!3HEZxJ@+%Qh}Z>jv*C{$p!i!8j}?a+@3A= zIAGwzjijN=FBi!|L1t?LM;Q;gkwn>2cAy-KV{dn nf0J1DIvEHQu*n~6U}x}qyky7vi4|9XhBJ7&`njxgN@xNA8m%nc diff --git a/test-with-vitest/coverage/lcov-report/sorter.js b/test-with-vitest/coverage/lcov-report/sorter.js deleted file mode 100644 index 2bb296a8..00000000 --- a/test-with-vitest/coverage/lcov-report/sorter.js +++ /dev/null @@ -1,196 +0,0 @@ -/* eslint-disable */ -var addSorting = (function() { - 'use strict'; - var cols, - currentSort = { - index: 0, - desc: false - }; - - // returns the summary table element - function getTable() { - return document.querySelector('.coverage-summary'); - } - // returns the thead element of the summary table - function getTableHeader() { - return getTable().querySelector('thead tr'); - } - // returns the tbody element of the summary table - function getTableBody() { - return getTable().querySelector('tbody'); - } - // returns the th element for nth column - function getNthColumn(n) { - return getTableHeader().querySelectorAll('th')[n]; - } - - function onFilterInput() { - const searchValue = document.getElementById('fileSearch').value; - const rows = document.getElementsByTagName('tbody')[0].children; - for (let i = 0; i < rows.length; i++) { - const row = rows[i]; - if ( - row.textContent - .toLowerCase() - .includes(searchValue.toLowerCase()) - ) { - row.style.display = ''; - } else { - row.style.display = 'none'; - } - } - } - - // loads the search box - function addSearchBox() { - var template = document.getElementById('filterTemplate'); - var templateClone = template.content.cloneNode(true); - templateClone.getElementById('fileSearch').oninput = onFilterInput; - template.parentElement.appendChild(templateClone); - } - - // loads all columns - function loadColumns() { - var colNodes = getTableHeader().querySelectorAll('th'), - colNode, - cols = [], - col, - i; - - for (i = 0; i < colNodes.length; i += 1) { - colNode = colNodes[i]; - col = { - key: colNode.getAttribute('data-col'), - sortable: !colNode.getAttribute('data-nosort'), - type: colNode.getAttribute('data-type') || 'string' - }; - cols.push(col); - if (col.sortable) { - col.defaultDescSort = col.type === 'number'; - colNode.innerHTML = - colNode.innerHTML + ''; - } - } - return cols; - } - // attaches a data attribute to every tr element with an object - // of data values keyed by column name - function loadRowData(tableRow) { - var tableCols = tableRow.querySelectorAll('td'), - colNode, - col, - data = {}, - i, - val; - for (i = 0; i < tableCols.length; i += 1) { - colNode = tableCols[i]; - col = cols[i]; - val = colNode.getAttribute('data-value'); - if (col.type === 'number') { - val = Number(val); - } - data[col.key] = val; - } - return data; - } - // loads all row data - function loadData() { - var rows = getTableBody().querySelectorAll('tr'), - i; - - for (i = 0; i < rows.length; i += 1) { - rows[i].data = loadRowData(rows[i]); - } - } - // sorts the table using the data for the ith column - function sortByIndex(index, desc) { - var key = cols[index].key, - sorter = function(a, b) { - a = a.data[key]; - b = b.data[key]; - return a < b ? -1 : a > b ? 1 : 0; - }, - finalSorter = sorter, - tableBody = document.querySelector('.coverage-summary tbody'), - rowNodes = tableBody.querySelectorAll('tr'), - rows = [], - i; - - if (desc) { - finalSorter = function(a, b) { - return -1 * sorter(a, b); - }; - } - - for (i = 0; i < rowNodes.length; i += 1) { - rows.push(rowNodes[i]); - tableBody.removeChild(rowNodes[i]); - } - - rows.sort(finalSorter); - - for (i = 0; i < rows.length; i += 1) { - tableBody.appendChild(rows[i]); - } - } - // removes sort indicators for current column being sorted - function removeSortIndicators() { - var col = getNthColumn(currentSort.index), - cls = col.className; - - cls = cls.replace(/ sorted$/, '').replace(/ sorted-desc$/, ''); - col.className = cls; - } - // adds sort indicators for current column being sorted - function addSortIndicators() { - getNthColumn(currentSort.index).className += currentSort.desc - ? ' sorted-desc' - : ' sorted'; - } - // adds event listeners for all sorter widgets - function enableUI() { - var i, - el, - ithSorter = function ithSorter(i) { - var col = cols[i]; - - return function() { - var desc = col.defaultDescSort; - - if (currentSort.index === i) { - desc = !currentSort.desc; - } - sortByIndex(i, desc); - removeSortIndicators(); - currentSort.index = i; - currentSort.desc = desc; - addSortIndicators(); - }; - }; - for (i = 0; i < cols.length; i += 1) { - if (cols[i].sortable) { - // add the click event handler on the th so users - // dont have to click on those tiny arrows - el = getNthColumn(i).querySelector('.sorter').parentElement; - if (el.addEventListener) { - el.addEventListener('click', ithSorter(i)); - } else { - el.attachEvent('onclick', ithSorter(i)); - } - } - } - } - // adds sorting functionality to the UI - return function() { - if (!getTable()) { - return; - } - cols = loadColumns(); - loadData(); - addSearchBox(); - addSortIndicators(); - enableUI(); - }; -})(); - -window.addEventListener('load', addSorting); diff --git a/test-with-vitest/coverage/lcov-report/src/data/connect-to-cosmos.ts.html b/test-with-vitest/coverage/lcov-report/src/data/connect-to-cosmos.ts.html deleted file mode 100644 index 8d316e4d..00000000 --- a/test-with-vitest/coverage/lcov-report/src/data/connect-to-cosmos.ts.html +++ /dev/null @@ -1,202 +0,0 @@ - - - - - - Code coverage report for src/data/connect-to-cosmos.ts - - - - - - - - - -
-
-

All files / src/data connect-to-cosmos.ts

-
- -
- 0% - Statements - 0/11 -
- - -
- 100% - Branches - 0/0 -
- - -
- 0% - Functions - 0/3 -
- - -
- 0% - Lines - 0/11 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
// connect-to-cosmos.ts
- 
-import { Container, CosmosClient } from '@azure/cosmos';
-import { DefaultAzureCredential } from '@azure/identity';
-import 'dotenv/config';
-import { v4 as uuidv4 } from 'uuid';
- 
-export { Container };
- 
-export default class CosmosConnector {
-  static connectToCosmosWithoutKey(): CosmosClient {
-    const endpoint = process.env.COSMOS_DB_ENDPOINT!;
-    const credential = new DefaultAzureCredential();
-    const client = new CosmosClient({ endpoint, aadCredentials: credential });
-    return client;
-  }
- 
-  static async connectToContainer(): Promise<Container> {
-    const client = CosmosConnector.connectToCosmosWithoutKey();
-    const databaseName = process.env.COSMOS_DATABASE_NAME!;
-    const containerName = process.env.COSMOS_CONTAINER_NAME!;
- 
-    // Ensure the database exists
-    const { database } = await client.databases.createIfNotExists({
-      id: databaseName,
-    });
- 
-    // Ensure the container exists
-    const { container } = await database.containers.createIfNotExists({
-      id: containerName,
-    });
- 
-    return container;
-  }
- 
-  static getUniqueId(): string {
-    return uuidv4();
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/test-with-vitest/coverage/lcov-report/src/data/fake-data.ts.html b/test-with-vitest/coverage/lcov-report/src/data/fake-data.ts.html deleted file mode 100644 index bce1d06e..00000000 --- a/test-with-vitest/coverage/lcov-report/src/data/fake-data.ts.html +++ /dev/null @@ -1,169 +0,0 @@ - - - - - - Code coverage report for src/data/fake-data.ts - - - - - - - - - -
-
-

All files / src/data fake-data.ts

-
- -
- 100% - Statements - 7/7 -
- - -
- 100% - Branches - 0/0 -
- - -
- 100% - Functions - 3/3 -
- - -
- 100% - Lines - 7/7 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29  -  -  -  -  -2x -  -  -  -2x -  -  -  -2x -2x -  -  -  -  -  -  -2x -2x -  -  -  -2x -  - 
import { v4 as uuidv4 } from 'uuid';
-import { faker } from '@faker-js/faker';
-import type { DbDocument, RawInput } from './model.js';
- 
-function createFixture<T>(): T {
-  const result = {
-    first: faker.person.firstName(),
-    last: faker.person.lastName(),
-  };
-  return result as T;
-}
- 
-export function createTestInput(): RawInput {
-  const { first, last } = createFixture<RawInput>();
-  return { id: uuidv4(), first, last };
-}
- 
-export function createTestInputAndResult(): {
-  input: RawInput;
-  result: Partial<DbDocument>;
-} {
-  const input = createTestInput();
-  const result = {
-    id: input.id,
-    name: `${input.first} ${input.last}`,
-  };
-  return { input, result };
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/test-with-vitest/coverage/lcov-report/src/data/index.html b/test-with-vitest/coverage/lcov-report/src/data/index.html deleted file mode 100644 index eab26ab5..00000000 --- a/test-with-vitest/coverage/lcov-report/src/data/index.html +++ /dev/null @@ -1,161 +0,0 @@ - - - - - - Code coverage report for src/data - - - - - - - - - -
-
-

All files src/data

-
- -
- 32.14% - Statements - 9/28 -
- - -
- 20% - Branches - 2/10 -
- - -
- 50% - Functions - 5/10 -
- - -
- 32.14% - Lines - 9/28 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FileStatementsBranchesFunctionsLines
connect-to-cosmos.ts -
-
0%0/11100%0/00%0/30%0/11
fake-data.ts -
-
100%7/7100%0/0100%3/3100%7/7
model.ts -
-
25%2/820%2/1066.66%2/325%2/8
verify.ts -
-
0%0/2100%0/00%0/10%0/2
-
-
-
- - - - - - - - \ No newline at end of file diff --git a/test-with-vitest/coverage/lcov-report/src/data/model.ts.html b/test-with-vitest/coverage/lcov-report/src/data/model.ts.html deleted file mode 100644 index a80af4be..00000000 --- a/test-with-vitest/coverage/lcov-report/src/data/model.ts.html +++ /dev/null @@ -1,202 +0,0 @@ - - - - - - Code coverage report for src/data/model.ts - - - - - - - - - -
-
-

All files / src/data model.ts

-
- -
- 25% - Statements - 2/8 -
- - -
- 20% - Branches - 2/10 -
- - -
- 66.66% - Functions - 2/3 -
- - -
- 25% - Lines - 2/8 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -1x -  -  -1x -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
// input-verified.ts
-export interface DbDocument {
-  id: string;
-  name: string;
-}
-export interface DbError {
-  message: string;
-  code: number;
-}
-export interface VerificationErrors {
-  message: string;
-}
-export interface RawInput {
-  id: string;
-  first: string;
-  last: string;
-}
- 
-export function isDbError(error: any): error is DbError {
-  return 'message' in error && 'code' in error;
-}
-export function isVerificationErrors(error: any): error is VerificationErrors {
-  return 'message' in error;
-}
-export function validateRawInput(input: any): string[] {
-  const errors: string[] = [];
- 
-  // eslint-disable-next-line @typescript-eslint/no-unsafe-call
-  if (typeof input.first !== 'string' || input.first.trim().length === 0) {
-    errors.push('First name is required and must be a non-empty string');
-  }
- 
-  // eslint-disable-next-line @typescript-eslint/no-unsafe-call
-  if (typeof input.last !== 'string' || input.last.trim().length === 0) {
-    errors.push('Last name is required and must be a non-empty string');
-  }
- 
-  return errors;
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/test-with-vitest/coverage/lcov-report/src/data/verify.ts.html b/test-with-vitest/coverage/lcov-report/src/data/verify.ts.html deleted file mode 100644 index bdee114d..00000000 --- a/test-with-vitest/coverage/lcov-report/src/data/verify.ts.html +++ /dev/null @@ -1,112 +0,0 @@ - - - - - - Code coverage report for src/data/verify.ts - - - - - - - - - -
-
-

All files / src/data verify.ts

-
- -
- 0% - Statements - 0/2 -
- - -
- 100% - Branches - 0/0 -
- - -
- 0% - Functions - 0/1 -
- - -
- 0% - Lines - 0/2 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10  -  -  -  -  -  -  -  -  - 
// input-verified.ts
-import { validateRawInput } from './model.js';
- 
-export default class Verfiy {
-  static inputVerified(doc: any): boolean {
-    const result = validateRawInput(doc);
-    return result.length === 0;
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/test-with-vitest/coverage/lcov-report/src/index.html b/test-with-vitest/coverage/lcov-report/src/index.html deleted file mode 100644 index 629fe7b3..00000000 --- a/test-with-vitest/coverage/lcov-report/src/index.html +++ /dev/null @@ -1,116 +0,0 @@ - - - - - - Code coverage report for src - - - - - - - - - -
-
-

All files src

-
- -
- 0% - Statements - 0/7 -
- - -
- 100% - Branches - 0/0 -
- - -
- 0% - Functions - 0/3 -
- - -
- 0% - Lines - 0/7 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FileStatementsBranchesFunctionsLines
index.ts -
-
0%0/7100%0/00%0/30%0/7
-
-
-
- - - - - - - - \ No newline at end of file diff --git a/test-with-vitest/coverage/lcov-report/src/index.ts.html b/test-with-vitest/coverage/lcov-report/src/index.ts.html deleted file mode 100644 index d2190b70..00000000 --- a/test-with-vitest/coverage/lcov-report/src/index.ts.html +++ /dev/null @@ -1,136 +0,0 @@ - - - - - - Code coverage report for src/index.ts - - - - - - - - - -
-
-

All files / src index.ts

-
- -
- 0% - Statements - 0/7 -
- - -
- 100% - Branches - 0/0 -
- - -
- 0% - Functions - 0/3 -
- - -
- 0% - Lines - 0/7 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  - 
import CosmosConnector from './data/connect-to-cosmos.js';
-import { createTestInput } from './data/fake-data.js';
-import type { DbDocument, DbError, VerificationErrors } from './data/model.js';
-import { insertDocument } from './lib/insert.js';
- 
-async function main(): Promise<DbDocument | DbError | VerificationErrors> {
-  const container = await CosmosConnector.connectToContainer();
-  const input = createTestInput();
-  return await insertDocument(container, input);
-}
- 
-main()
-  .then((doc) => console.log(doc))
-  .catch((error) => {
-    console.error(error);
-    process.exit(1);
-  });
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/test-with-vitest/coverage/lcov-report/src/lib/index.html b/test-with-vitest/coverage/lcov-report/src/lib/index.html deleted file mode 100644 index bc5f95a7..00000000 --- a/test-with-vitest/coverage/lcov-report/src/lib/index.html +++ /dev/null @@ -1,116 +0,0 @@ - - - - - - Code coverage report for src/lib - - - - - - - - - -
-
-

All files src/lib

-
- -
- 72.72% - Statements - 8/11 -
- - -
- 50% - Branches - 3/6 -
- - -
- 100% - Functions - 1/1 -
- - -
- 72.72% - Lines - 8/11 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FileStatementsBranchesFunctionsLines
insert.ts -
-
72.72%8/1150%3/6100%1/172.72%8/11
-
-
-
- - - - - - - - \ No newline at end of file diff --git a/test-with-vitest/coverage/lcov-report/src/lib/insert.ts.html b/test-with-vitest/coverage/lcov-report/src/lib/insert.ts.html deleted file mode 100644 index 0641e1d3..00000000 --- a/test-with-vitest/coverage/lcov-report/src/lib/insert.ts.html +++ /dev/null @@ -1,208 +0,0 @@ - - - - - - Code coverage report for src/lib/insert.ts - - - - - - - - - -
-
-

All files / src/lib insert.ts

-
- -
- 72.72% - Statements - 8/11 -
- - -
- 50% - Branches - 3/6 -
- - -
- 100% - Functions - 1/1 -
- - -
- 72.72% - Lines - 8/11 -
- - -
-

- Press n or j to go to the next uncovered block, b, p or k for the previous block. -

- -
-
-

-
1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42  -  -  -  -  -  -  -  -  -  -  -  -  -  -3x -  -3x -1x -  -  -2x -2x -  -  -  -  -1x -  -1x -  -  -  -  -  -  -  -  -1x -  -  -  - 
// insertDocument.ts
-import { Container } from '../data/connect-to-cosmos.js';
-import type {   
-  DbDocument,
-  DbError, 
-  RawInput,
-  VerificationErrors, 
-} from '../data/model.js';
-import Verify from '../data/verify.js';
- 
-export async function insertDocument(
-  container: Container,
-  doc: RawInput,
-): Promise<DbDocument | DbError | VerificationErrors> {
-  const isVerified: boolean = Verify.inputVerified(doc);
- 
-  if (!isVerified) {
-    return { message: 'Verification failed' } as VerificationErrors;
-  }
- 
-  try {
-    const { resource } = await container.items.create({
-      id: doc.id,
-      name: `${doc.first} ${doc.last}`,
-    });
- 
-    return resource as DbDocument;
-  } catch (error: any) {
-    Iif (error instanceof Error) {
-      if ((error as any).code === 409) {
-        return {
-          message: 'Insertion failed: Duplicate entry',
-          code: 409,
-        } as DbError;
-      }
-      return { message: error.message, code: (error as any).code } as DbError;
-    } else {
-      return { message: 'An unknown error occurred', code: 500 } as DbError;
-    }
-  }
-}
- 
- -
-
- - - - - - - - \ No newline at end of file diff --git a/test-with-vitest/tests/boilerplate.test.ts b/test-with-vitest/tests/boilerplate.test.ts index 7d604114..8f97ca50 100644 --- a/test-with-vitest/tests/boilerplate.test.ts +++ b/test-with-vitest/tests/boilerplate.test.ts @@ -1,8 +1,9 @@ -import { describe, it, beforeEach, afterEach, expect } from 'vitest'; +import { describe, it, beforeEach, afterEach, expect, vi } from 'vitest'; describe('boilerplate', () => { beforeEach(() => { // Setup required before each test + vi.restoreAllMocks(); }); afterEach(() => { diff --git a/test-with-vitest/tests/insert.test.ts b/test-with-vitest/tests/insert.test.ts index 0ee73478..7a3d8e30 100644 --- a/test-with-vitest/tests/insert.test.ts +++ b/test-with-vitest/tests/insert.test.ts @@ -1,56 +1,75 @@ -import { describe, it, beforeEach, expect, vi, Mock } from 'vitest'; -import { Container } from '../src/data/connect-to-cosmos.js'; +import { describe, it, beforeEach, expect, vi } from 'vitest'; +import type { Container, ItemResponse } from '@azure/cosmos'; +import { insertDocument } from '../src/lib/insert.js'; import { createTestInputAndResult } from '../src/data/fake-data.js'; -import { +import type { DbDocument, DbError, + RawInput +} from '../src/data/model.js'; +import { isDbError, isVerificationErrors, - RawInput, } from '../src/data/model.js'; import Verify from '../src/data/verify.js'; -import { insertDocument } from '../src/lib/insert.js'; - -// Mock app dependencies for Cosmos DB setup -vi.mock('../app/data/connect-to-cosmos', () => ({ - connectToContainer: vi.fn(), - getUniqueId: vi.fn().mockReturnValue('unique-id'), -})); -// Mock app dependencies for input verification -vi.mock('../app/data/verify', () => { - return { - default: class { - static inputVerified = vi.fn(); - }, - }; -}); -describe('SDK', () => { - // Declare the Cosmos DB Container mock. - let mockContainer: Container; +describe('insertDocument', () => { + let fakeContainer: Container; beforeEach(() => { - // Clear all mocks before each test. - vi.clearAllMocks(); - - // Create a fake container with a mocked create method. - mockContainer = { + vi.restoreAllMocks(); + // Create a fake container with a mocked `create` function + fakeContainer = { items: { create: vi.fn(), }, } as unknown as Container; }); + it('should insert document successfully and validate the create call', async () => { + // Prepare test data + const { input, result }: { input: RawInput; result: Partial } = + createTestInputAndResult(); + + // Set up the mocked return value. + // Here we "cast" our minimal object to satisfy the expected type ItemResponse. + (fakeContainer.items.create as unknown as ReturnType).mockResolvedValue({ + resource: result, + // Minimal additional properties required by ItemResponse. + item: result, + headers: {}, + statusCode: 201, + diagnostics: {} as any, + requestCharge: 0, + activityId: 'fake-activity-id', + } as unknown as ItemResponse); + + // Call the function under test that internally calls container.items.create. + const insertDocumentResult = await insertDocument(fakeContainer, input); + + // Validate the returned value. + expect(insertDocumentResult).toEqual(result); + + // Validate that create was called once with the proper arguments. + expect(fakeContainer.items.create).toHaveBeenCalledTimes(1); + expect( + (fakeContainer.items.create as unknown as ReturnType).mock.calls[0][0] + ).toEqual({ + id: input.id, + name: result.name, + }); + }); + it('should return verification error if input is not verified', async () => { // Arrange – mock the input verification function to return false. - vi.spyOn(Verify, "inputVerified").mockReturnValue(false); - // Arrange – provide a doc with an incorrect shape. + vi.mocked(Verify.inputVerified).mockReturnValue(false); + const doc = { name: 'test' }; // Act – call the function under test. const insertDocumentResult = await insertDocument( - mockContainer, + fakeContainer, doc as unknown as RawInput, ); @@ -64,64 +83,6 @@ describe('SDK', () => { } // Assert – behavior verification: ensure create method was not called. - expect((mockContainer.items.create as vi.Mock)).not.toHaveBeenCalled(); - }); - - it('should insert document successfully', async () => { - // Arrange – create input and expected result data. - const { input, result }: { input: RawInput; result: Partial } = - createTestInputAndResult(); - - // Arrange – mock the input verification function to return true. - vi.spyOn(Verify, "inputVerified").mockReturnValue(true); - - // Arrange – mock the create method to resolve with our expected result. - const mockedCreate = mockContainer.items.create as unknown as Mock; - mockedCreate.mockImplementation(async () => ({ resource: result })); - - // Act – call the function under test. - const insertDocumentResult = await insertDocument(mockContainer, input); - - // Assert – state verification: check the result is as expected. - expect(insertDocumentResult).toEqual(result); - - // Assert – behavior verification: ensure create was called once with correct arguments. - expect(mockedCreate).toHaveBeenCalledTimes(1); - expect(mockedCreate.mock.calls[0][0]).toEqual({ - id: input.id, - name: result.name, - }); - }); - - it('should return error if db insert fails', async () => { - // Arrange – create input and expected result data. - const { input, result } = createTestInputAndResult(); - - // Arrange – mock the input verification to return true. - vi.spyOn(Verify, "inputVerified").mockReturnValue(true); - - // Arrange – mock the create method to reject with an error. - const mockError: DbError = { - message: 'An unknown error occurred', - code: 500, - }; - (mockContainer.items.create as vi.Mock).mockRejectedValue(mockError); - - // Act – call the function under test. - const insertDocumentResult = await insertDocument(mockContainer, input); - - // Assert – verify result is of type DbError. - if (isDbError(insertDocumentResult)) { - expect(insertDocumentResult.message).toBe(mockError.message); - } else { - throw new Error('Result is not of type DbError'); - } - - // Assert – behavior verification: ensure create was called once with correct arguments. - expect((mockContainer.items.create as vi.Mock)).toHaveBeenCalledTimes(1); - expect((mockContainer.items.create as vi.Mock).mock.calls[0][0]).toEqual({ - id: input.id, - name: result.name, - }); + expect(fakeContainer.items.create).not.toHaveBeenCalled(); }); }); \ No newline at end of file diff --git a/test-with-vitest/tests/insert.test.ts.old b/test-with-vitest/tests/insert.test.ts.old new file mode 100644 index 00000000..70531529 --- /dev/null +++ b/test-with-vitest/tests/insert.test.ts.old @@ -0,0 +1,132 @@ +import { describe, it, beforeEach, expect, vi } from 'vitest'; +import { Container } from '../src/data/connect-to-cosmos.js'; +import { createTestInputAndResult } from '../src/data/fake-data.js'; +import { + DbDocument, + DbError, + isDbError, + isVerificationErrors, + RawInput, +} from '../src/data/model.js'; +import Verify from '../src/data/verify.js'; +import { insertDocument } from '../src/lib/insert.js'; + +// Mock app dependencies for Cosmos DB setup +vi.mock('../app/data/connect-to-cosmos', () => ({ + connectToContainer: vi.fn(), + getUniqueId: vi.fn().mockReturnValue('unique-id'), +})); + +// Mock app dependencies for input verification +vi.mock('../app/data/verify', () => { + return { + default: class { + static inputVerified = vi.fn(); + }, + }; +}); + +describe('SDK', () => { + + beforeEach(() => { + // Clear all mocks before each test. + vi.clearAllMocks(); + + }); + + it('should return verification error if input is not verified', async () => { + // Arrange – mock the input verification function to return false. + vi.mocked(Verify.inputVerified).mockReturnValue(false); + + const fakeContainer = { + items: { + create: vi.fn(), + } + } as unknown as Container; + + const createFunction = vi.spyOn(fakeContainer.items, 'create'); + createFunction.mockRejectedValue({ + message: 'Verification failed', + }); + + const doc = { name: 'test' }; + + // Act – call the function under test. + const insertDocumentResult = await insertDocument( + fakeContainer, + doc as unknown as RawInput, + ); + + // Assert – state verification: result should indicate verification failure. + if (isVerificationErrors(insertDocumentResult)) { + expect(insertDocumentResult).toEqual({ + message: 'Verification failed', + }); + } else { + throw new Error('Result is not of type VerificationErrors'); + } + + // Assert – behavior verification: ensure create method was not called. + expect(createFunction).not.toHaveBeenCalled(); + }); + + it('should insert document successfully', async () => { + const { input, result }: { input: RawInput; result: Partial } = createTestInputAndResult(); + vi.mocked(Verify.inputVerified).mockReturnValue(true); + + const myContainer = { + items: { + create: vi.fn(), + }, + } as unknown as Container; + const mockedContainer = vi.mock(myContainer.items.create); + mockedContainer.mockResolvedValue({ resource: result }); + + const insertDocumentResult = await insertDocument(mockedContainer, input); + expect(insertDocumentResult).toEqual(result); + + expect(mockedContainer).toHaveBeenCalledTimes(1); + expect(mockedContainer.calls[0][0]).toEqual({ + id: input.id, + name: result.name, + }); + }); + + it('should return error if db insert fails', async () => { + // Arrange – create input and expected result data. + const { input, result } = createTestInputAndResult(); + + // Arrange – mock the input verification to return true. + vi.mocked(Verify.inputVerified).mockReturnValue(true); + + // Arrange – mock the create method to reject with an error. + const mockError: DbError = { + message: 'An unknown error occurred', + code: 500, + }; + const myContainer = { + items: { + create: vi.fn(), + }, + } as unknown as Container; + const mockedContainer = vi.mock(myContainer.items.create); + mockedContainer.mockRejectedValue(mockError); + + // Act – call the function under test. + const insertDocumentResult = await insertDocument(mockedContainer, input); + + // Assert – verify result is of type DbError. + if (isDbError(insertDocumentResult)) { + expect(insertDocumentResult.message).toBe(mockError.message); + } else { + throw new Error('Result is not of type DbError'); + } + + // Assert – behavior verification: ensure create was called once with correct arguments. + expect(mockedContainer).toHaveBeenCalledTimes(1); + expect(mockedContainer.mock.calls[0][0]).toEqual({ + id: input.id, + name: result.name, + }); + }); +}); \ No newline at end of file From 0b095028f48299118cb852a3f243d2b8500148db Mon Sep 17 00:00:00 2001 From: Dina Berry Date: Fri, 14 Mar 2025 22:22:04 +0000 Subject: [PATCH 17/27] test pass --- test-with-vitest/coverage/lcov.info | 56 +++++++++++++-------------- test-with-vitest/tests/insert.test.ts | 7 +++- 2 files changed, 34 insertions(+), 29 deletions(-) diff --git a/test-with-vitest/coverage/lcov.info b/test-with-vitest/coverage/lcov.info index 574cefa1..d046a913 100644 --- a/test-with-vitest/coverage/lcov.info +++ b/test-with-vitest/coverage/lcov.info @@ -53,16 +53,16 @@ FN:13,createTestInput FN:18,createTestInputAndResult FNF:3 FNH:3 -FNDA:2,createFixture -FNDA:2,createTestInput -FNDA:2,createTestInputAndResult -DA:6,2 -DA:10,2 -DA:14,2 -DA:15,2 -DA:22,2 -DA:23,2 -DA:27,2 +FNDA:1,createFixture +FNDA:1,createTestInput +FNDA:1,createTestInputAndResult +DA:6,1 +DA:10,1 +DA:14,1 +DA:15,1 +DA:22,1 +DA:23,1 +DA:27,1 LF:7 LH:7 BRF:0 @@ -74,11 +74,11 @@ FN:19,isDbError FN:22,isVerificationErrors FN:25,validateRawInput FNF:3 -FNH:2 -FNDA:1,isDbError +FNH:1 +FNDA:0,isDbError FNDA:1,isVerificationErrors FNDA:0,validateRawInput -DA:20,1 +DA:20,0 DA:23,1 DA:26,0 DA:29,0 @@ -87,9 +87,9 @@ DA:34,0 DA:35,0 DA:38,0 LF:8 -LH:2 -BRDA:20,0,0,1 -BRDA:20,0,1,1 +LH:1 +BRDA:20,0,0,0 +BRDA:20,0,1,0 BRDA:29,1,0,0 BRDA:29,1,1,0 BRDA:29,2,0,0 @@ -99,7 +99,7 @@ BRDA:34,3,1,0 BRDA:34,4,0,0 BRDA:34,4,1,0 BRF:10 -BRH:2 +BRH:0 end_of_record TN: SF:src/data/verify.ts @@ -119,26 +119,26 @@ SF:src/lib/insert.ts FN:11,insertDocument FNF:1 FNH:1 -FNDA:3,insertDocument -DA:15,3 -DA:17,3 +FNDA:2,insertDocument +DA:15,2 +DA:17,2 DA:18,1 -DA:21,2 -DA:22,2 +DA:21,1 +DA:22,1 DA:27,1 -DA:29,1 +DA:29,0 DA:30,0 DA:31,0 DA:36,0 -DA:38,1 +DA:38,0 LF:11 -LH:8 +LH:6 BRDA:17,0,0,1 -BRDA:17,0,1,2 +BRDA:17,0,1,1 BRDA:29,1,0,0 -BRDA:29,1,1,1 +BRDA:29,1,1,0 BRDA:30,2,0,0 BRDA:30,2,1,0 BRF:6 -BRH:3 +BRH:2 end_of_record diff --git a/test-with-vitest/tests/insert.test.ts b/test-with-vitest/tests/insert.test.ts index 7a3d8e30..aa54403f 100644 --- a/test-with-vitest/tests/insert.test.ts +++ b/test-with-vitest/tests/insert.test.ts @@ -31,6 +31,8 @@ describe('insertDocument', () => { // Prepare test data const { input, result }: { input: RawInput; result: Partial } = createTestInputAndResult(); + const inputVerifiedMock = vi.spyOn(Verify, 'inputVerified'); + inputVerifiedMock.mockReturnValue(true); // Set up the mocked return value. // Here we "cast" our minimal object to satisfy the expected type ItemResponse. @@ -52,6 +54,7 @@ describe('insertDocument', () => { expect(insertDocumentResult).toEqual(result); // Validate that create was called once with the proper arguments. + expect(inputVerifiedMock).toHaveBeenCalledTimes(1); expect(fakeContainer.items.create).toHaveBeenCalledTimes(1); expect( (fakeContainer.items.create as unknown as ReturnType).mock.calls[0][0] @@ -63,7 +66,8 @@ describe('insertDocument', () => { it('should return verification error if input is not verified', async () => { // Arrange – mock the input verification function to return false. - vi.mocked(Verify.inputVerified).mockReturnValue(false); + const inputVerifiedMock = vi.spyOn(Verify, 'inputVerified'); + inputVerifiedMock.mockReturnValue(false); const doc = { name: 'test' }; @@ -84,5 +88,6 @@ describe('insertDocument', () => { // Assert – behavior verification: ensure create method was not called. expect(fakeContainer.items.create).not.toHaveBeenCalled(); + expect(inputVerifiedMock).toHaveBeenCalledTimes(1); }); }); \ No newline at end of file From 3af4dc70fbe2762f7acb106bd1d491f4da3fdac2 Mon Sep 17 00:00:00 2001 From: Dina Berry Date: Fri, 14 Mar 2025 22:44:36 +0000 Subject: [PATCH 18/27] vitest inserts work --- test-with-vitest/coverage/lcov.info | 144 -------------------------- test-with-vitest/tests/insert.test.ts | 44 +++++++- 2 files changed, 42 insertions(+), 146 deletions(-) delete mode 100644 test-with-vitest/coverage/lcov.info diff --git a/test-with-vitest/coverage/lcov.info b/test-with-vitest/coverage/lcov.info deleted file mode 100644 index d046a913..00000000 --- a/test-with-vitest/coverage/lcov.info +++ /dev/null @@ -1,144 +0,0 @@ -TN: -SF:src/index.ts -FN:6,main -FN:13,(anonymous_1) -FN:14,(anonymous_2) -FNF:3 -FNH:0 -FNDA:0,main -FNDA:0,(anonymous_1) -FNDA:0,(anonymous_2) -DA:7,0 -DA:8,0 -DA:9,0 -DA:12,0 -DA:13,0 -DA:15,0 -DA:16,0 -LF:7 -LH:0 -BRF:0 -BRH:0 -end_of_record -TN: -SF:src/data/connect-to-cosmos.ts -FN:11,(anonymous_0) -FN:18,(anonymous_1) -FN:36,(anonymous_2) -FNF:3 -FNH:0 -FNDA:0,(anonymous_0) -FNDA:0,(anonymous_1) -FNDA:0,(anonymous_2) -DA:12,0 -DA:13,0 -DA:14,0 -DA:15,0 -DA:19,0 -DA:20,0 -DA:21,0 -DA:24,0 -DA:29,0 -DA:33,0 -DA:37,0 -LF:11 -LH:0 -BRF:0 -BRH:0 -end_of_record -TN: -SF:src/data/fake-data.ts -FN:5,createFixture -FN:13,createTestInput -FN:18,createTestInputAndResult -FNF:3 -FNH:3 -FNDA:1,createFixture -FNDA:1,createTestInput -FNDA:1,createTestInputAndResult -DA:6,1 -DA:10,1 -DA:14,1 -DA:15,1 -DA:22,1 -DA:23,1 -DA:27,1 -LF:7 -LH:7 -BRF:0 -BRH:0 -end_of_record -TN: -SF:src/data/model.ts -FN:19,isDbError -FN:22,isVerificationErrors -FN:25,validateRawInput -FNF:3 -FNH:1 -FNDA:0,isDbError -FNDA:1,isVerificationErrors -FNDA:0,validateRawInput -DA:20,0 -DA:23,1 -DA:26,0 -DA:29,0 -DA:30,0 -DA:34,0 -DA:35,0 -DA:38,0 -LF:8 -LH:1 -BRDA:20,0,0,0 -BRDA:20,0,1,0 -BRDA:29,1,0,0 -BRDA:29,1,1,0 -BRDA:29,2,0,0 -BRDA:29,2,1,0 -BRDA:34,3,0,0 -BRDA:34,3,1,0 -BRDA:34,4,0,0 -BRDA:34,4,1,0 -BRF:10 -BRH:0 -end_of_record -TN: -SF:src/data/verify.ts -FN:5,(anonymous_0) -FNF:1 -FNH:0 -FNDA:0,(anonymous_0) -DA:6,0 -DA:7,0 -LF:2 -LH:0 -BRF:0 -BRH:0 -end_of_record -TN: -SF:src/lib/insert.ts -FN:11,insertDocument -FNF:1 -FNH:1 -FNDA:2,insertDocument -DA:15,2 -DA:17,2 -DA:18,1 -DA:21,1 -DA:22,1 -DA:27,1 -DA:29,0 -DA:30,0 -DA:31,0 -DA:36,0 -DA:38,0 -LF:11 -LH:6 -BRDA:17,0,0,1 -BRDA:17,0,1,1 -BRDA:29,1,0,0 -BRDA:29,1,1,0 -BRDA:30,2,0,0 -BRDA:30,2,1,0 -BRF:6 -BRH:2 -end_of_record diff --git a/test-with-vitest/tests/insert.test.ts b/test-with-vitest/tests/insert.test.ts index aa54403f..ed3935ae 100644 --- a/test-with-vitest/tests/insert.test.ts +++ b/test-with-vitest/tests/insert.test.ts @@ -1,5 +1,5 @@ import { describe, it, beforeEach, expect, vi } from 'vitest'; -import type { Container, ItemResponse } from '@azure/cosmos'; +import { ConflictResolutionMode, type Container, type ItemResponse } from '@azure/cosmos'; import { insertDocument } from '../src/lib/insert.js'; import { createTestInputAndResult } from '../src/data/fake-data.js'; import type { @@ -81,7 +81,7 @@ describe('insertDocument', () => { if (isVerificationErrors(insertDocumentResult)) { expect(insertDocumentResult).toEqual({ message: 'Verification failed', - }); + } as unknown as DbError); } else { throw new Error('Result is not of type VerificationErrors'); } @@ -90,4 +90,44 @@ describe('insertDocument', () => { expect(fakeContainer.items.create).not.toHaveBeenCalled(); expect(inputVerifiedMock).toHaveBeenCalledTimes(1); }); + + it('should return error if db insert fails', async () => { + // Arrange – create input and expected result data. + const { input, result } = createTestInputAndResult(); + + // Arrange – mock the input verification to return true. + const inputVerifiedMock = vi.spyOn(Verify, 'inputVerified'); + inputVerifiedMock.mockReturnValue(true); + + // Arrange – mock the create method to reject with an error. + const mockError: DbError = { + message: 'An unknown error occurred', + code: 500, + }; + + (fakeContainer.items.create as unknown as ReturnType).mockRejectedValue(mockError as unknown as DbError); + + // Act – call the function under test. + const insertDocumentResult = await insertDocument(fakeContainer, input); + console.log(insertDocumentResult); + + // Assert – verify result is of type DbError. + if (isDbError(insertDocumentResult)) { + expect(insertDocumentResult.message).toBe(mockError.message); + } else { + throw new Error('Result is not of type DbError'); + } + + // Assert – behavior verification: ensure create was called once with correct arguments. + expect(inputVerifiedMock).toHaveBeenCalledTimes(1); + expect(fakeContainer.items.create).toHaveBeenCalledTimes(1); + expect( + (fakeContainer.items.create as unknown as ReturnType).mock.calls[0][0] + ).toEqual({ + id: input.id, + name: result.name, + }); + }); + + }); \ No newline at end of file From 8aba049df2d598ad56b30ba714513b1f9534a274 Mon Sep 17 00:00:00 2001 From: Dina Berry Date: Fri, 14 Mar 2025 23:27:05 +0000 Subject: [PATCH 19/27] 1:1 --- test-with-jest/src/fakes/fake-in-mem-db.spec.ts | 8 ++++---- test-with-jest/src/mock-function/lib/insert.spec.ts | 1 - 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/test-with-jest/src/fakes/fake-in-mem-db.spec.ts b/test-with-jest/src/fakes/fake-in-mem-db.spec.ts index 7c03a34c..365a5ab8 100644 --- a/test-with-jest/src/fakes/fake-in-mem-db.spec.ts +++ b/test-with-jest/src/fakes/fake-in-mem-db.spec.ts @@ -21,7 +21,6 @@ function someTestFunction(db: FakeDatabase, key: string, value: any): any { return db.get(key); } -// Jest test suite describe('someTestFunction', () => { let fakeDb: FakeDatabase; let testKey: string; @@ -36,8 +35,6 @@ describe('someTestFunction', () => { lastUpdated: new Date().toISOString(), }; - // Spy on the save method - jest.spyOn(fakeDb, 'save'); }); afterEach(() => { @@ -46,7 +43,10 @@ describe('someTestFunction', () => { }); test('should save and return the correct value', () => { - // Perform test + // Spy on the save method + jest.spyOn(fakeDb, 'save'); + + // Call the function under test. const result = someTestFunction(fakeDb, testKey, testValue); // Verify state diff --git a/test-with-jest/src/mock-function/lib/insert.spec.ts b/test-with-jest/src/mock-function/lib/insert.spec.ts index 5c5bf3e7..5082e864 100644 --- a/test-with-jest/src/mock-function/lib/insert.spec.ts +++ b/test-with-jest/src/mock-function/lib/insert.spec.ts @@ -25,7 +25,6 @@ jest.mock('../data/verify', () => ({ })); describe('SDK', () => { - // Mock the Cosmo DB Container object let mockContainer: jest.Mocked; beforeEach(() => { From 15f1cb60f26216ed20230c60ad5ca76b6db4661f Mon Sep 17 00:00:00 2001 From: Dina Berry Date: Fri, 14 Mar 2025 23:28:15 +0000 Subject: [PATCH 20/27] 1:1 --- .../test/boilerplate-with-mock.test.ts | 7 +- .../test/fake-in-mem-db.test.ts | 3 +- test-with-node-testrunner/test/insert.test.ts | 74 +++++++++---------- .../tests/boilerplate-with-mock.test.ts | 7 +- test-with-vitest/tests/boilerplate.test.ts | 4 - test-with-vitest/tests/insert.test.ts | 61 ++++++++------- 6 files changed, 82 insertions(+), 74 deletions(-) diff --git a/test-with-node-testrunner/test/boilerplate-with-mock.test.ts b/test-with-node-testrunner/test/boilerplate-with-mock.test.ts index da8e0f5a..2b6e8755 100644 --- a/test-with-node-testrunner/test/boilerplate-with-mock.test.ts +++ b/test-with-node-testrunner/test/boilerplate-with-mock.test.ts @@ -1,3 +1,4 @@ +// boilerplate-with-mock.test.ts import { describe, it, @@ -28,13 +29,15 @@ describe('boilerplate with mock', () => { it('should if ', async () => { - // Replace the original implementation with a mock, returning 2 + // Arrange: Replace the original implementation with a mock, returning 2 const m = mock.method(MyService, "myFunction").mock; m.mockImplementation(async () => Promise.resolve(2)) - // Test the function, but get the mocked value + // Act: Test the function, but get the mocked value const result = await MyService.myFunction(); + + // Assert assert.strictEqual(result, 2 ) diff --git a/test-with-node-testrunner/test/fake-in-mem-db.test.ts b/test-with-node-testrunner/test/fake-in-mem-db.test.ts index 7bdc80c9..e986dbce 100644 --- a/test-with-node-testrunner/test/fake-in-mem-db.test.ts +++ b/test-with-node-testrunner/test/fake-in-mem-db.test.ts @@ -1,3 +1,4 @@ +// fake-in-mem-db.spec.ts import { describe, it, beforeEach, afterEach, mock } from 'node:test'; import assert from 'node:assert'; @@ -56,7 +57,7 @@ describe('In-Mem DB', () => { assert.strictEqual(result.last, 'Jones'); assert.strictEqual(result.lastUpdated, testValue.lastUpdated); - // Verify behavior using the spy. + // Verify behavior assert.strictEqual(saveSpy.callCount(), 1); const calls = saveSpy.calls; assert.deepStrictEqual(calls[0].arguments, [testKey, testValue]); diff --git a/test-with-node-testrunner/test/insert.test.ts b/test-with-node-testrunner/test/insert.test.ts index a813790a..d1cce825 100644 --- a/test-with-node-testrunner/test/insert.test.ts +++ b/test-with-node-testrunner/test/insert.test.ts @@ -1,6 +1,7 @@ // insertDocument.test.ts import { describe, it, beforeEach, mock } from 'node:test'; import assert from 'node:assert'; + import { Container } from '../src/data/connect-to-cosmos'; import { createTestInputAndResult } from '../src/data/fake-data'; import type { @@ -12,53 +13,19 @@ import { isDbError, isVerificationErrors, } from '../src/data/model'; -// Instead of jest.mock, import the whole module to override functions as needed. + import Verify from '../src/data/verify'; import CosmosConnector from '../src/data/connect-to-cosmos'; import { insertDocument } from '../src/lib/insert'; -// --- Test suite for insertDocument --- + describe('SDK', () => { - // Set up a fresh container object before each test. beforeEach(() => { - // Setup required before each test + // Clear all mocks before each test mock.restoreAll() }) - it('should insert document successfully', async () => { - // Arrange: override inputVerified to return true. - const { input, result }: { input: RawInput; result: Partial } = createTestInputAndResult(); - - const fakeContainer = { - items: { - create: async (doc: any) => { - return { resource: result }; - }, - }, - } as unknown as Container; - - const mVerify = mock.method(Verify, "inputVerified").mock; - mVerify.mockImplementation(() => true); - - const mContainerCreate = mock.method(fakeContainer.items as any, "create").mock; - mContainerCreate.mockImplementation(async (doc: any) => { - return { resource: result }; - }); - - // Act: - const receivedResult = await insertDocument(fakeContainer, input); - - // Assert - State verification: Ensure the result is as expected. - assert.deepStrictEqual(receivedResult, result); - - // Assert - Behavior verification: Ensure create was called once with correct arguments. - assert.strictEqual(mContainerCreate.callCount(), 1); - assert.deepStrictEqual(mContainerCreate.calls[0].arguments[0], { - id: input.id, - name: result.name, - }); - }); it('should return verification error if input is not verified', async () => { const fakeContainer = { @@ -94,6 +61,39 @@ describe('SDK', () => { assert.strictEqual(mContainerCreate.callCount(), 0); }); + it('should insert document successfully', async () => { + // Arrange: override inputVerified to return true. + const { input, result }: { input: RawInput; result: Partial } = createTestInputAndResult(); + + const fakeContainer = { + items: { + create: async (doc: any) => { + return { resource: result }; + }, + }, + } as unknown as Container; + + const mVerify = mock.method(Verify, "inputVerified").mock; + mVerify.mockImplementation(() => true); + + const mContainerCreate = mock.method(fakeContainer.items as any, "create").mock; + mContainerCreate.mockImplementation(async (doc: any) => { + return { resource: result }; + }); + + // Act: + const receivedResult = await insertDocument(fakeContainer, input); + + // Assert - State verification: Ensure the result is as expected. + assert.deepStrictEqual(receivedResult, result); + + // Assert - Behavior verification: Ensure create was called once with correct arguments. + assert.strictEqual(mContainerCreate.callCount(), 1); + assert.deepStrictEqual(mContainerCreate.calls[0].arguments[0], { + id: input.id, + name: result.name, + }); + }); it('should return error if db insert fails', async () => { // Arrange: override inputVerified to return true. const { input, result } = createTestInputAndResult(); diff --git a/test-with-vitest/tests/boilerplate-with-mock.test.ts b/test-with-vitest/tests/boilerplate-with-mock.test.ts index 2794142e..c23839c5 100644 --- a/test-with-vitest/tests/boilerplate-with-mock.test.ts +++ b/test-with-vitest/tests/boilerplate-with-mock.test.ts @@ -1,3 +1,4 @@ +// boilerplate-with-mock.test.ts import { describe, it, beforeEach, afterEach, expect, vi } from 'vitest'; const result = 1; @@ -19,11 +20,13 @@ describe('boilerplate with mock', () => { }); it('should if ', async () => { - // Replace the original implementation with a mock, returning 2. + // Arrange: Replace the original implementation with a mock, returning 2. vi.spyOn(MyService, 'myFunction').mockResolvedValue(2); - // Test the function: it should now return the mocked value. + // Act: Test the function: it should now return the mocked value. const resultVal = await MyService.myFunction(); + + // Assert expect(resultVal).toBe(2); }); }); \ No newline at end of file diff --git a/test-with-vitest/tests/boilerplate.test.ts b/test-with-vitest/tests/boilerplate.test.ts index 8f97ca50..eae12f97 100644 --- a/test-with-vitest/tests/boilerplate.test.ts +++ b/test-with-vitest/tests/boilerplate.test.ts @@ -3,7 +3,6 @@ import { describe, it, beforeEach, afterEach, expect, vi } from 'vitest'; describe('boilerplate', () => { beforeEach(() => { // Setup required before each test - vi.restoreAllMocks(); }); afterEach(() => { @@ -20,8 +19,5 @@ describe('boilerplate', () => { // Assert // - check the state: result returned from function // - check the behavior: dependency function calls - - // Example assertion: - expect(true).toBe(true); }); }); \ No newline at end of file diff --git a/test-with-vitest/tests/insert.test.ts b/test-with-vitest/tests/insert.test.ts index ed3935ae..73f290cd 100644 --- a/test-with-vitest/tests/insert.test.ts +++ b/test-with-vitest/tests/insert.test.ts @@ -1,5 +1,7 @@ +// insertDocument.test.ts import { describe, it, beforeEach, expect, vi } from 'vitest'; -import { ConflictResolutionMode, type Container, type ItemResponse } from '@azure/cosmos'; +import type { Container, ItemResponse } from '@azure/cosmos'; + import { insertDocument } from '../src/lib/insert.js'; import { createTestInputAndResult } from '../src/data/fake-data.js'; import type { @@ -18,7 +20,9 @@ describe('insertDocument', () => { let fakeContainer: Container; beforeEach(() => { + // Clear all mocks before each test vi.restoreAllMocks(); + // Create a fake container with a mocked `create` function fakeContainer = { items: { @@ -27,7 +31,34 @@ describe('insertDocument', () => { } as unknown as Container; }); - it('should insert document successfully and validate the create call', async () => { + it('should return verification error if input is not verified', async () => { + // Arrange – mock the input verification function to return false. + const inputVerifiedMock = vi.spyOn(Verify, 'inputVerified'); + inputVerifiedMock.mockReturnValue(false); + + const doc = { name: 'test' }; + + // Act – call the function under test. + const insertDocumentResult = await insertDocument( + fakeContainer, + doc as unknown as RawInput, + ); + + // Assert – state verification: result should indicate verification failure. + if (isVerificationErrors(insertDocumentResult)) { + expect(insertDocumentResult).toEqual({ + message: 'Verification failed', + } as unknown as DbError); + } else { + throw new Error('Result is not of type VerificationErrors'); + } + + // Assert – behavior verification: ensure create method was not called. + expect(fakeContainer.items.create).not.toHaveBeenCalled(); + expect(inputVerifiedMock).toHaveBeenCalledTimes(1); + }); + + it('should insert document successfully', async () => { // Prepare test data const { input, result }: { input: RawInput; result: Partial } = createTestInputAndResult(); @@ -64,32 +95,7 @@ describe('insertDocument', () => { }); }); - it('should return verification error if input is not verified', async () => { - // Arrange – mock the input verification function to return false. - const inputVerifiedMock = vi.spyOn(Verify, 'inputVerified'); - inputVerifiedMock.mockReturnValue(false); - - const doc = { name: 'test' }; - - // Act – call the function under test. - const insertDocumentResult = await insertDocument( - fakeContainer, - doc as unknown as RawInput, - ); - // Assert – state verification: result should indicate verification failure. - if (isVerificationErrors(insertDocumentResult)) { - expect(insertDocumentResult).toEqual({ - message: 'Verification failed', - } as unknown as DbError); - } else { - throw new Error('Result is not of type VerificationErrors'); - } - - // Assert – behavior verification: ensure create method was not called. - expect(fakeContainer.items.create).not.toHaveBeenCalled(); - expect(inputVerifiedMock).toHaveBeenCalledTimes(1); - }); it('should return error if db insert fails', async () => { // Arrange – create input and expected result data. @@ -109,7 +115,6 @@ describe('insertDocument', () => { // Act – call the function under test. const insertDocumentResult = await insertDocument(fakeContainer, input); - console.log(insertDocumentResult); // Assert – verify result is of type DbError. if (isDbError(insertDocumentResult)) { From 4f414b00896dbbe737d1094c5b9567a2bce4e7de Mon Sep 17 00:00:00 2001 From: Dina Berry Date: Mon, 17 Mar 2025 15:01:47 +0000 Subject: [PATCH 21/27] don't touch unit-testing --- test-with-node-testrunner/test/01-spies.test.ts | 5 +++++ test-with-vitest/tests/01-spies.test.ts | 2 +- unit-testing/package.json | 4 ++-- unit-testing/src/mock-function/lib/insert.spec.ts | 2 +- unit-testing/tsconfig.json | 4 ++-- 5 files changed, 11 insertions(+), 6 deletions(-) diff --git a/test-with-node-testrunner/test/01-spies.test.ts b/test-with-node-testrunner/test/01-spies.test.ts index 8bcbea13..02da8fc7 100644 --- a/test-with-node-testrunner/test/01-spies.test.ts +++ b/test-with-node-testrunner/test/01-spies.test.ts @@ -15,6 +15,11 @@ function run({fn, times}){ describe('Spies', () => { it('should verify calls in a mock', () => { const spy = mock.fn(); + spy.mock.mockImplementation((arg) => { + console.log(arg); + }); + + run({fn: spy, times: 3}); assert.strictEqual(spy.mock.callCount(), 3); diff --git a/test-with-vitest/tests/01-spies.test.ts b/test-with-vitest/tests/01-spies.test.ts index 3ce68d1d..76a946ee 100644 --- a/test-with-vitest/tests/01-spies.test.ts +++ b/test-with-vitest/tests/01-spies.test.ts @@ -1,4 +1,4 @@ -import { describe, it, expect, vi } from 'vitest'; +import { it, expect, vi } from 'vitest'; function run({ fn, times }: { fn: (arg: { current: number }) => void; times: number }) { for (let i = 0; i < times; i++) { diff --git a/unit-testing/package.json b/unit-testing/package.json index d3e876ff..ab9a874e 100644 --- a/unit-testing/package.json +++ b/unit-testing/package.json @@ -6,8 +6,8 @@ "scripts": { "clear": "rm -rf dist && rm -rf coverage", "start": "node dist/mock-function/index.js", - "test": "jest --detectOpenHandles dist", - "test:coverage": "jest --detectOpenHandles dist --coverage", + "test": "jest dist", + "test:coverage": "jest dist --coverage", "build": "npm run clear && tsc", "lint": "eslint \"./src/**/*.ts\" --fix", "format": "prettier \"./src/**/*.ts\" --write", diff --git a/unit-testing/src/mock-function/lib/insert.spec.ts b/unit-testing/src/mock-function/lib/insert.spec.ts index a7d206d7..659c37a4 100644 --- a/unit-testing/src/mock-function/lib/insert.spec.ts +++ b/unit-testing/src/mock-function/lib/insert.spec.ts @@ -22,7 +22,7 @@ jest.mock('../data/verify', () => ({ inputVerified: jest.fn(), })); -describe('SDK', () => { +describe('insertDocument', () => { // Mock the Cosmo DB Container object let mockContainer: jest.Mocked; diff --git a/unit-testing/tsconfig.json b/unit-testing/tsconfig.json index 9f817d53..237b8c78 100644 --- a/unit-testing/tsconfig.json +++ b/unit-testing/tsconfig.json @@ -6,7 +6,7 @@ "esModuleInterop": true, // Enables emit interoperability between CommonJS and ES Modules "forceConsistentCasingInFileNames": true, // Disallow inconsistently-cased references to the same file "types": ["jest", "node"], // Specify type package - "outDir": "dist", - "rootDir": "src", + "outDir": "./dist", + "rootDir": "./src", }, } \ No newline at end of file From 9e1eaf9fba809bf9f7b34d601fff415b696cbe74 Mon Sep 17 00:00:00 2001 From: "Dina Berry (MSFT)" Date: Tue, 18 Mar 2025 08:39:39 -0700 Subject: [PATCH 22/27] Apply suggestions from code review Co-authored-by: Yohan Lasorsa Signed-off-by: Dina Berry (MSFT) --- test-with-jest/src/mock-function/data/verify.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-with-jest/src/mock-function/data/verify.ts b/test-with-jest/src/mock-function/data/verify.ts index d71969d5..6af904bc 100644 --- a/test-with-jest/src/mock-function/data/verify.ts +++ b/test-with-jest/src/mock-function/data/verify.ts @@ -3,5 +3,5 @@ import { validateRawInput } from './model'; export function inputVerified(doc: any): boolean { const result = validateRawInput(doc); - return result.length === 0 ? true : false; + return result.length === 0; } From 4e141e806638f8004e2720b3d3f5886b6989bfd9 Mon Sep 17 00:00:00 2001 From: Dina Berry Date: Tue, 18 Mar 2025 16:58:36 +0000 Subject: [PATCH 23/27] az and cosmos resources with mi --- .devcontainer/devcontainer.json | 3 +- test-with-jest/scripts/create-resources.sh | 80 ++++++++++++++-------- 2 files changed, 54 insertions(+), 29 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index df4881e9..c647c124 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -5,7 +5,8 @@ // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile "image": "mcr.microsoft.com/devcontainers/javascript-node:1-22-bullseye", "features": { - "ghcr.io/devcontainers-contrib/features/npm-package:1": {} + "ghcr.io/devcontainers-contrib/features/npm-package:1": {}, + "ghcr.io/devcontainers/features/azure-cli:1": {} }, "customizations": { "vscode": { diff --git a/test-with-jest/scripts/create-resources.sh b/test-with-jest/scripts/create-resources.sh index ff9ffe2f..dfdcd83a 100644 --- a/test-with-jest/scripts/create-resources.sh +++ b/test-with-jest/scripts/create-resources.sh @@ -1,55 +1,79 @@ #!/bin/bash +# filepath: create-resources.sh -# Prerequisites: -# Azure CLI installed -# `az login` +# Prerequisites: +# - Install the Azure CLI and run: az login +# +# This script now fetches the role definition id for "Cosmos DB Operator" automatically. -# Read .env file in the script -set -a -source ../.env +# Exit if any command returns a non-zero status +# set -euo + +# Verify that the user is logged in +if ! az account show > /dev/null 2>&1; then + echo "Error: Not logged in to Azure CLI. Please run 'az login' and try again." + exit 1 +fi random_string() { - cat /dev/urandom | tr -dc 'a-z0-9' | fold -w 10 | head -n 1 + tr -dc 'a-z0-9' > ../.env +# Append the endpoint to the .env file if not already populated +echo "COSMOS_DB_ENDPOINT=$COSMOS_DB_ENDPOINT" >> .env echo "Cosmos DB Endpoint: $COSMOS_DB_ENDPOINT" + +# Dynamically fetch the role definition id for "Cosmos DB Operator" +ROLE_DEFINITION_ID=$(az role definition list --name "Cosmos DB Operator" --query "[0].id" -o tsv) +printf "Role Definition ID: %s\n" "$ROLE_DEFINITION_ID" + + +printf "Pricipal ID: %s\n" "$PRINCIPAL_ID" + +# Create a role assignment using the fetched role definition. +az role assignment create \ + --assignee "$PRINCIPAL_ID" \ + --role "$ROLE_DEFINITION_ID" \ + --scope "/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RG" +printf "Role assignment created\n" From 1e00940ac9166defc274f169b1946a0a46a20eda Mon Sep 17 00:00:00 2001 From: Dina Berry Date: Tue, 18 Mar 2025 21:39:27 +0000 Subject: [PATCH 24/27] Updates based on feedback --- .../scripts => scripts}/create-resources.sh | 54 ++++++++++++++----- test-with-jest/src/mock-function/index.ts | 17 +++--- test-with-node-testrunner/package.json | 5 +- test-with-node-testrunner/src/index.ts | 17 +++--- test-with-node-testrunner/test/insert.test.ts | 14 ++--- test-with-node-testrunner/tsconfig.json | 4 +- test-with-vitest/package.json | 2 +- test-with-vitest/src/index.ts | 18 +++---- 8 files changed, 75 insertions(+), 56 deletions(-) rename {test-with-jest/scripts => scripts}/create-resources.sh (51%) diff --git a/test-with-jest/scripts/create-resources.sh b/scripts/create-resources.sh similarity index 51% rename from test-with-jest/scripts/create-resources.sh rename to scripts/create-resources.sh index dfdcd83a..15024a91 100644 --- a/test-with-jest/scripts/create-resources.sh +++ b/scripts/create-resources.sh @@ -23,8 +23,8 @@ printf "Random String: %s\n" "$RANDOM_STRING" SUBSCRIPTION_ID=$(az account show --query id -o tsv) LOCATION="eastus" -RESOURCE_GROUP_NAME="cosmosdb-rg-$RANDOM_STRING" -COSMOS_DB_RESOURCE_NAME="cosmosdb-$RANDOM_STRING" +RESOURCE_GROUP_NAME="sdk-test-cosmosdb-rg-$RANDOM_STRING" +COSMOS_DB_RESOURCE_NAME="sdk-test-cosmosdb-$RANDOM_STRING" PRINCIPAL_ID=$(az ad signed-in-user show --query id -o tsv) # Create a unique resource group and cosmos db name using a random suffix. @@ -46,7 +46,7 @@ az cosmosdb create \ --subscription "$SUBSCRIPTION_ID" \ --resource-group "$RG" \ --name "$RESOURCE_NAME" \ - --kind MongoDB \ + --kind GlobalDocumentDB \ --locations regionName="$LOCATION" failoverPriority=0 isZoneRedundant=False printf "Cosmos DB account created\n" @@ -61,19 +61,47 @@ printf "Cosmos DB Endpoint: %s\n" "$COSMOS_DB_ENDPOINT" # Append the endpoint to the .env file if not already populated echo "COSMOS_DB_ENDPOINT=$COSMOS_DB_ENDPOINT" >> .env - echo "Cosmos DB Endpoint: $COSMOS_DB_ENDPOINT" -# Dynamically fetch the role definition id for "Cosmos DB Operator" -ROLE_DEFINITION_ID=$(az role definition list --name "Cosmos DB Operator" --query "[0].id" -o tsv) -printf "Role Definition ID: %s\n" "$ROLE_DEFINITION_ID" +# -------------------------------------------------------------------------- +# Create a Cosmos DB SQL database and container, and update .env with their values -printf "Pricipal ID: %s\n" "$PRINCIPAL_ID" +# Set database and container names +DB_NAME="db-$RANDOM_STRING" +CONTAINER_NAME="container-$RANDOM_STRING" + +# Create a database (using throughput of 400 RU/s as an example) +az cosmosdb sql database create \ + --account-name "$RESOURCE_NAME" \ + --name "$DB_NAME" \ + --resource-group "$RG" \ + --subscription "$SUBSCRIPTION_ID" \ + --throughput 400 +echo "COSMOS_DATABASE_NAME=$DB_NAME" >> .env +printf "Cosmos DB SQL database '%s' created\n" "$DB_NAME" + +# Create a container (using throughput of 400 RU/s as an example) +az cosmosdb sql container create \ + --account-name "$RESOURCE_NAME" \ + --database-name "$DB_NAME" \ + --name "$CONTAINER_NAME" \ + --partition-key-path "/name" \ + --resource-group "$RG" \ + --subscription "$SUBSCRIPTION_ID" \ + --throughput 400 +echo "COSMOS_CONTAINER_NAME=$CONTAINER_NAME" >> .env +printf "Cosmos DB SQL container '%s' created\n" "$CONTAINER_NAME" + +printf "Database and container names appended to .env\n" +-------------------------------------------------------------------------- + +# Role definition id for "Cosmos DB Operator" # Create a role assignment using the fetched role definition. -az role assignment create \ - --assignee "$PRINCIPAL_ID" \ - --role "$ROLE_DEFINITION_ID" \ - --scope "/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RG" -printf "Role assignment created\n" +az cosmosdb sql role assignment create \ + --resource-group "$RG" \ + --account-name "$RESOURCE_NAME" \ + --role-definition-id "/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RG/providers/Microsoft.DocumentDB/databaseAccounts/$RESOURCE_NAME/sqlRoleDefinitions/00000000-0000-0000-0000-000000000002" \ + --principal-id "$PRINCIPAL_ID" \ + --scope "/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RG/providers/Microsoft.DocumentDB/databaseAccounts/$RESOURCE_NAME" \ No newline at end of file diff --git a/test-with-jest/src/mock-function/index.ts b/test-with-jest/src/mock-function/index.ts index 1fb12681..b4eac299 100644 --- a/test-with-jest/src/mock-function/index.ts +++ b/test-with-jest/src/mock-function/index.ts @@ -3,15 +3,12 @@ import { createTestInput } from './data/fake-data'; import { DbDocument, DbError, VerificationErrors } from './data/model'; import { insertDocument } from './lib/insert'; -async function main(): Promise { +try { const container = await connectToContainer(); const input = createTestInput(); - return await insertDocument(container, input); -} - -main() - .then((doc) => console.log(doc)) - .catch((error) => { - console.error(error); - process.exit(1); - }); + const result = await insertDocument(container, input); + console.log(result); +} catch (error) { + console.error(error); + process.exit(1); +} \ No newline at end of file diff --git a/test-with-node-testrunner/package.json b/test-with-node-testrunner/package.json index c57547f0..5e9e395d 100644 --- a/test-with-node-testrunner/package.json +++ b/test-with-node-testrunner/package.json @@ -2,18 +2,19 @@ "name": "test-with-node-testrunner", "version": "1.0.0", "main": "index.js", + "type": "module", "scripts": { "build": "rm -rf dist && tsc", "test": "npm run build && node --test --experimental-test-coverage --experimental-test-module-mocks --trace-exit" }, "devDependencies": { + "@faker-js/faker": "^8.4.1", "@types/node": "^22.13.10" }, "dependencies": { "@azure/cosmos": "^4.1.0", "@azure/identity": "^4.4.1", "dotenv": "^16.4.5", - "uuid": "^10.0.0", - "@faker-js/faker": "^8.4.1" + "uuid": "^10.0.0" } } diff --git a/test-with-node-testrunner/src/index.ts b/test-with-node-testrunner/src/index.ts index cf9b01ac..1370fa14 100644 --- a/test-with-node-testrunner/src/index.ts +++ b/test-with-node-testrunner/src/index.ts @@ -1,17 +1,14 @@ import CosmosConnector from './data/connect-to-cosmos.js'; import { createTestInput } from './data/fake-data.js'; -import type { DbDocument, DbError, VerificationErrors } from './data/model.js'; import { insertDocument } from './lib/insert.js'; -async function main(): Promise { +try { const container = await CosmosConnector.connectToContainer(); const input = createTestInput(); - return await insertDocument(container, input); + const result = await insertDocument(container, input); + console.log(result); } - -main() - .then((doc) => console.log(doc)) - .catch((error) => { - console.error(error); - process.exit(1); - }); +catch (error) { + console.error(error); + process.exit(1); +} \ No newline at end of file diff --git a/test-with-node-testrunner/test/insert.test.ts b/test-with-node-testrunner/test/insert.test.ts index d1cce825..20771eb4 100644 --- a/test-with-node-testrunner/test/insert.test.ts +++ b/test-with-node-testrunner/test/insert.test.ts @@ -2,21 +2,21 @@ import { describe, it, beforeEach, mock } from 'node:test'; import assert from 'node:assert'; -import { Container } from '../src/data/connect-to-cosmos'; -import { createTestInputAndResult } from '../src/data/fake-data'; +import { Container } from '../src/data/connect-to-cosmos.js'; +import { createTestInputAndResult } from '../src/data/fake-data.js'; import type { DbDocument, DbError, RawInput, -} from '../src/data/model'; +} from '../src/data/model.js'; import { isDbError, isVerificationErrors, -} from '../src/data/model'; +} from '../src/data/model.js'; -import Verify from '../src/data/verify'; -import CosmosConnector from '../src/data/connect-to-cosmos'; -import { insertDocument } from '../src/lib/insert'; +import Verify from '../src/data/verify.js'; +import CosmosConnector from '../src/data/connect-to-cosmos.js'; +import { insertDocument } from '../src/lib/insert.js'; describe('SDK', () => { diff --git a/test-with-node-testrunner/tsconfig.json b/test-with-node-testrunner/tsconfig.json index e8be0f7f..309c77eb 100644 --- a/test-with-node-testrunner/tsconfig.json +++ b/test-with-node-testrunner/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { - "target": "es6", // Specify ECMAScript target version - "module": "commonjs", // Specify module code generation + "target": "ESNext", // Specify ECMAScript target version + "module": "NodeNext", // Specify module code generation "strict": true, // Enable all strict type-checking options "esModuleInterop": true, // Enables emit interoperability between CommonJS and ES Modules "noImplicitAny": false, // Enable error reporting for expressions and declarations with an implied 'any' type diff --git a/test-with-vitest/package.json b/test-with-vitest/package.json index 31c8cc7c..0294231d 100644 --- a/test-with-vitest/package.json +++ b/test-with-vitest/package.json @@ -13,13 +13,13 @@ "license": "ISC", "description": "", "devDependencies": { + "@faker-js/faker": "^8.4.1", "@vitest/coverage-istanbul": "^3.0.8", "vitest": "^3.0.8" }, "dependencies": { "@azure/cosmos": "^4.1.0", "@azure/identity": "^4.4.1", - "@faker-js/faker": "^8.4.1", "dotenv": "^16.4.5", "uuid": "^10.0.0" } diff --git a/test-with-vitest/src/index.ts b/test-with-vitest/src/index.ts index cf9b01ac..68dcfa7e 100644 --- a/test-with-vitest/src/index.ts +++ b/test-with-vitest/src/index.ts @@ -1,17 +1,13 @@ import CosmosConnector from './data/connect-to-cosmos.js'; import { createTestInput } from './data/fake-data.js'; -import type { DbDocument, DbError, VerificationErrors } from './data/model.js'; import { insertDocument } from './lib/insert.js'; - -async function main(): Promise { - const container = await CosmosConnector.connectToContainer(); - const input = createTestInput(); - return await insertDocument(container, input); +try { + const container = await CosmosConnector.connectToContainer(); + const input = createTestInput(); + const result = await insertDocument(container, input); + console.log(result); } - -main() - .then((doc) => console.log(doc)) - .catch((error) => { +catch (error) { console.error(error); process.exit(1); - }); +} From 92e73fbc3d72aad4d9783bd9fee9736adf997b96 Mon Sep 17 00:00:00 2001 From: Dina Berry Date: Tue, 18 Mar 2025 21:42:51 +0000 Subject: [PATCH 25/27] Update readme files --- scripts/{create-resources.sh => create-cosmos-db-resources.sh} | 0 test-with-jest/README.md | 2 ++ test-with-node-testrunner/README.md | 2 ++ test-with-vitest/README.md | 2 ++ 4 files changed, 6 insertions(+) rename scripts/{create-resources.sh => create-cosmos-db-resources.sh} (100%) diff --git a/scripts/create-resources.sh b/scripts/create-cosmos-db-resources.sh similarity index 100% rename from scripts/create-resources.sh rename to scripts/create-cosmos-db-resources.sh diff --git a/test-with-jest/README.md b/test-with-jest/README.md index db5aeb98..f97924a1 100644 --- a/test-with-jest/README.md +++ b/test-with-jest/README.md @@ -2,6 +2,8 @@ This subfolder is the source code for the [How to Test Azure SDK Integration in JavaScript Applications](https://learn.microsoft.com/azure/developer/javascript/sdk/test-sdk-integration). The purpose is to demonstrate unit test mocks for the Azure SDK for JavaScript. These specific examples use Azure Cosmos DB. +Use Azure CLI to create Cosmos DB resource if you intend to insert a document with the application code against the cloud resource. [Script](../scripts/create-cosmos-db-resources.sh) + ## To run the test 1. `npm install` diff --git a/test-with-node-testrunner/README.md b/test-with-node-testrunner/README.md index 6b66e35c..b045a3d1 100644 --- a/test-with-node-testrunner/README.md +++ b/test-with-node-testrunner/README.md @@ -2,6 +2,8 @@ This subfolder is the source code for the [How to Test Azure SDK Integration in JavaScript Applications](https://learn.microsoft.com/azure/developer/javascript/sdk/test-sdk-integration). The purpose is to demonstrate unit test mocks for the Azure SDK for JavaScript. These specific examples use Azure Cosmos DB. +Use Azure CLI to create Cosmos DB resource if you intend to insert a document with the application code against the cloud resource. [Script](../scripts/create-cosmos-db-resources.sh) + ## To run the test 1. `npm install` diff --git a/test-with-vitest/README.md b/test-with-vitest/README.md index 55e5d1bc..51d28e8c 100644 --- a/test-with-vitest/README.md +++ b/test-with-vitest/README.md @@ -2,6 +2,8 @@ This subfolder is the source code for the [How to Test Azure SDK Integration in JavaScript Applications](https://learn.microsoft.com/azure/developer/javascript/sdk/test-sdk-integration). The purpose is to demonstrate unit test mocks for the Azure SDK for JavaScript. These specific examples use Azure Cosmos DB. +Use Azure CLI to create Cosmos DB resource if you intend to insert a document with the application code against the cloud resource. [Script](../scripts/create-cosmos-db-resources.sh) + ## To run the test 1. `npm install` From 9539a96f32cc6371aec2969d0f7a47e33bb51858 Mon Sep 17 00:00:00 2001 From: Dina Berry Date: Tue, 18 Mar 2025 21:44:55 +0000 Subject: [PATCH 26/27] remove old file --- test-with-vitest/tests/insert.test.ts.old | 132 ---------------------- 1 file changed, 132 deletions(-) delete mode 100644 test-with-vitest/tests/insert.test.ts.old diff --git a/test-with-vitest/tests/insert.test.ts.old b/test-with-vitest/tests/insert.test.ts.old deleted file mode 100644 index 70531529..00000000 --- a/test-with-vitest/tests/insert.test.ts.old +++ /dev/null @@ -1,132 +0,0 @@ -import { describe, it, beforeEach, expect, vi } from 'vitest'; -import { Container } from '../src/data/connect-to-cosmos.js'; -import { createTestInputAndResult } from '../src/data/fake-data.js'; -import { - DbDocument, - DbError, - isDbError, - isVerificationErrors, - RawInput, -} from '../src/data/model.js'; -import Verify from '../src/data/verify.js'; -import { insertDocument } from '../src/lib/insert.js'; - -// Mock app dependencies for Cosmos DB setup -vi.mock('../app/data/connect-to-cosmos', () => ({ - connectToContainer: vi.fn(), - getUniqueId: vi.fn().mockReturnValue('unique-id'), -})); - -// Mock app dependencies for input verification -vi.mock('../app/data/verify', () => { - return { - default: class { - static inputVerified = vi.fn(); - }, - }; -}); - -describe('SDK', () => { - - beforeEach(() => { - // Clear all mocks before each test. - vi.clearAllMocks(); - - }); - - it('should return verification error if input is not verified', async () => { - // Arrange – mock the input verification function to return false. - vi.mocked(Verify.inputVerified).mockReturnValue(false); - - const fakeContainer = { - items: { - create: vi.fn(), - } - } as unknown as Container; - - const createFunction = vi.spyOn(fakeContainer.items, 'create'); - createFunction.mockRejectedValue({ - message: 'Verification failed', - }); - - const doc = { name: 'test' }; - - // Act – call the function under test. - const insertDocumentResult = await insertDocument( - fakeContainer, - doc as unknown as RawInput, - ); - - // Assert – state verification: result should indicate verification failure. - if (isVerificationErrors(insertDocumentResult)) { - expect(insertDocumentResult).toEqual({ - message: 'Verification failed', - }); - } else { - throw new Error('Result is not of type VerificationErrors'); - } - - // Assert – behavior verification: ensure create method was not called. - expect(createFunction).not.toHaveBeenCalled(); - }); - - it('should insert document successfully', async () => { - const { input, result }: { input: RawInput; result: Partial } = createTestInputAndResult(); - vi.mocked(Verify.inputVerified).mockReturnValue(true); - - const myContainer = { - items: { - create: vi.fn(), - }, - } as unknown as Container; - const mockedContainer = vi.mock(myContainer.items.create); - mockedContainer.mockResolvedValue({ resource: result }); - - const insertDocumentResult = await insertDocument(mockedContainer, input); - expect(insertDocumentResult).toEqual(result); - - expect(mockedContainer).toHaveBeenCalledTimes(1); - expect(mockedContainer.calls[0][0]).toEqual({ - id: input.id, - name: result.name, - }); - }); - - it('should return error if db insert fails', async () => { - // Arrange – create input and expected result data. - const { input, result } = createTestInputAndResult(); - - // Arrange – mock the input verification to return true. - vi.mocked(Verify.inputVerified).mockReturnValue(true); - - // Arrange – mock the create method to reject with an error. - const mockError: DbError = { - message: 'An unknown error occurred', - code: 500, - }; - const myContainer = { - items: { - create: vi.fn(), - }, - } as unknown as Container; - const mockedContainer = vi.mock(myContainer.items.create); - mockedContainer.mockRejectedValue(mockError); - - // Act – call the function under test. - const insertDocumentResult = await insertDocument(mockedContainer, input); - - // Assert – verify result is of type DbError. - if (isDbError(insertDocumentResult)) { - expect(insertDocumentResult.message).toBe(mockError.message); - } else { - throw new Error('Result is not of type DbError'); - } - - // Assert – behavior verification: ensure create was called once with correct arguments. - expect(mockedContainer).toHaveBeenCalledTimes(1); - expect(mockedContainer.mock.calls[0][0]).toEqual({ - id: input.id, - name: result.name, - }); - }); -}); \ No newline at end of file From d70cab56530508ce892e23022dd271402b104363 Mon Sep 17 00:00:00 2001 From: Dina Berry Date: Wed, 26 Mar 2025 12:29:11 -0700 Subject: [PATCH 27/27] Updates based on Maor's review --- test-with-jest/.editorconfig => .editorconfig | 0 package-lock.json | 99 ++++++++++--------- test-with-jest/package.json | 21 ++-- test-with-jest/sample.env | 8 +- .../src/fakes/fake-in-mem-db.spec.ts | 3 +- .../mock-function/data/connect-to-cosmos.ts | 4 +- .../src/mock-function/data/fake-data.ts | 18 +++- test-with-jest/src/mock-function/index.ts | 25 +++-- .../src/mock-function/lib/insert.spec.ts | 13 +-- .../boilerplate-with-mock.spec.ts | 2 +- test-with-node-testrunner/.eslintrc.json | 29 ++++++ test-with-node-testrunner/package.json | 18 +++- .../src/data/connect-to-cosmos.ts | 4 +- .../src/data/fake-data.ts | 18 +++- test-with-node-testrunner/src/index.ts | 5 +- test-with-node-testrunner/src/lib/insert.ts | 6 +- .../test/01-spies.test.ts | 43 ++++---- .../test/02-stubs.test.ts | 9 +- .../test/boilerplate-with-mock.test.ts | 61 +++++------- .../test/boilerplate.test.ts | 31 +++--- .../test/fake-in-mem-db.test.ts | 2 +- test-with-node-testrunner/test/insert.test.ts | 47 ++++----- test-with-vitest/.eslintrc.json | 29 ++++++ test-with-vitest/package.json | 17 +++- .../src/data/connect-to-cosmos.ts | 4 +- test-with-vitest/src/data/fake-data.ts | 18 +++- test-with-vitest/src/index.ts | 15 ++- test-with-vitest/src/lib/insert.ts | 6 +- test-with-vitest/tests/01-spies.test.ts | 25 +++-- test-with-vitest/tests/02-stubs.test.ts | 16 ++- .../tests/boilerplate-with-mock.test.ts | 2 +- test-with-vitest/tests/boilerplate.test.ts | 4 +- test-with-vitest/tests/fake-in-mem-db.test.ts | 2 +- test-with-vitest/tests/insert.test.ts | 32 +++--- test-with-vitest/vitest.config.ts | 2 +- unit-testing/.editorconfig | 10 -- 36 files changed, 350 insertions(+), 298 deletions(-) rename test-with-jest/.editorconfig => .editorconfig (100%) create mode 100644 test-with-node-testrunner/.eslintrc.json create mode 100644 test-with-vitest/.eslintrc.json delete mode 100644 unit-testing/.editorconfig diff --git a/test-with-jest/.editorconfig b/.editorconfig similarity index 100% rename from test-with-jest/.editorconfig rename to .editorconfig diff --git a/package-lock.json b/package-lock.json index 889cca19..68b498f9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1315,22 +1315,6 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@faker-js/faker": { - "version": "8.4.1", - "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-8.4.1.tgz", - "integrity": "sha512-XQ3cU+Q8Uqmrbf2e0cIC/QN43sTBSC8KF12u29Mb47tWrt2hAgBXSgpZMj4Ao8Uk0iJcU99QsOCaIL8934obCg==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/fakerjs" - } - ], - "license": "MIT", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0", - "npm": ">=6.14.13" - } - }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -2470,13 +2454,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/uuid": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", - "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/yargs": { "version": "17.0.33", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", @@ -8834,19 +8811,6 @@ "punycode": "^2.1.0" } }, - "node_modules/uuid": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", - "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/v8-to-istanbul": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", @@ -9674,20 +9638,16 @@ "dependencies": { "@azure/cosmos": "^4.1.0", "@azure/identity": "^4.4.1", - "@faker-js/faker": "^8.4.1", - "dotenv": "^16.4.5", - "uuid": "^10.0.0" + "dotenv": "^16.4.5" }, "devDependencies": { "@types/jest": "^29.5.12", "@types/node": "^22.4.0", - "@types/uuid": "^10.0.0", "@typescript-eslint/eslint-plugin": "^8.2.0", "eslint": "^8.56.0", "eslint-config-airbnb-base": "^15.0.0", "eslint-plugin-import": "^2.29.1", "eslint-plugin-jest": "^28.8.0", - "husky": "^9.0.10", "jest": "^29.7.0", "prettier": "^3.2.4", "typescript": "^5.5.4" @@ -9714,12 +9674,33 @@ "dependencies": { "@azure/cosmos": "^4.1.0", "@azure/identity": "^4.4.1", - "@faker-js/faker": "^8.4.1", - "dotenv": "^16.4.5", - "uuid": "^10.0.0" + "dotenv": "^16.4.5" }, "devDependencies": { - "@types/node": "^22.13.10" + "@types/node": "^22.13.10", + "@typescript-eslint/eslint-plugin": "^8.2.0", + "eslint": "^8.56.0", + "eslint-config-airbnb-base": "^15.0.0", + "eslint-plugin-import": "^2.29.1", + "eslint-plugin-jest": "^28.8.0", + "prettier": "^3.2.4", + "typescript": "^5.5.4" + } + }, + "test-with-node-testrunner/node_modules/prettier": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", + "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" } }, "test-with-vitest": { @@ -9728,15 +9709,37 @@ "dependencies": { "@azure/cosmos": "^4.1.0", "@azure/identity": "^4.4.1", - "@faker-js/faker": "^8.4.1", - "dotenv": "^16.4.5", - "uuid": "^10.0.0" + "dotenv": "^16.4.5" }, "devDependencies": { + "@types/node": "^22.13.10", + "@typescript-eslint/eslint-plugin": "^8.2.0", "@vitest/coverage-istanbul": "^3.0.8", + "eslint": "^8.56.0", + "eslint-config-airbnb-base": "^15.0.0", + "eslint-plugin-import": "^2.29.1", + "eslint-plugin-jest": "^28.8.0", + "prettier": "^3.2.4", + "typescript": "^5.5.4", "vitest": "^3.0.8" } }, + "test-with-vitest/node_modules/prettier": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", + "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "testrunner": { "version": "1.0.0", "extraneous": true, diff --git a/test-with-jest/package.json b/test-with-jest/package.json index ccf2b14f..3193a83c 100644 --- a/test-with-jest/package.json +++ b/test-with-jest/package.json @@ -4,12 +4,14 @@ "main": "index.js", "scripts": { "clear": "rm -rf dist && rm -rf coverage", + "prestart": "npm run build", "start": "node dist/mock-function/index.js", - "test": "jest --detectOpenHandles dist --coverage", - "build": "npm run clear && tsc", + "test": "npm run build && npm run test:jest", + "test:jest": "jest --detectOpenHandles dist --coverage", + "prebuild": "npm run format && npm run lint", + "build": "tsc", "lint": "eslint \"./src/**/*.ts\" --fix", - "format": "prettier \"./src/**/*.ts\" --write", - "precommit": "npm run format && git add . && npm run lint" + "format": "prettier --write './src/**/*.{ts,tsx}" }, "keywords": [], "author": "", @@ -17,27 +19,18 @@ "dependencies": { "@azure/cosmos": "^4.1.0", "@azure/identity": "^4.4.1", - "dotenv": "^16.4.5", - "uuid": "^10.0.0", - "@faker-js/faker": "^8.4.1" + "dotenv": "^16.4.5" }, "devDependencies": { "@types/jest": "^29.5.12", "@types/node": "^22.4.0", - "@types/uuid": "^10.0.0", "@typescript-eslint/eslint-plugin": "^8.2.0", "eslint": "^8.56.0", "eslint-config-airbnb-base": "^15.0.0", "eslint-plugin-import": "^2.29.1", "eslint-plugin-jest": "^28.8.0", - "husky": "^9.0.10", "jest": "^29.7.0", "prettier": "^3.2.4", "typescript": "^5.5.4" - }, - "husky": { - "hooks": { - "pre-commit": "npm run precommit" - } } } diff --git a/test-with-jest/sample.env b/test-with-jest/sample.env index e1da034c..edd7a3a0 100644 --- a/test-with-jest/sample.env +++ b/test-with-jest/sample.env @@ -1,8 +1,8 @@ SUBSCRIPTION_ID= -RESOURCE_GROUP_NAME="my-resource-group -LOCATION=eastus2 -COSMOS_DATABASE_NAME="my-db" -COSMOS_CONTAINER_NAME="my-container" +RESOURCE_GROUP_NAME= +LOCATION= +COSMOS_DATABASE_NAME= +COSMOS_CONTAINER_NAME= ## ./scripts/create-resources.sh adds the COSMOS DB endpoint # The endpoint URL for your Azure Cosmos DB account diff --git a/test-with-jest/src/fakes/fake-in-mem-db.spec.ts b/test-with-jest/src/fakes/fake-in-mem-db.spec.ts index 365a5ab8..45a390d3 100644 --- a/test-with-jest/src/fakes/fake-in-mem-db.spec.ts +++ b/test-with-jest/src/fakes/fake-in-mem-db.spec.ts @@ -34,12 +34,11 @@ describe('someTestFunction', () => { last: 'Jones', lastUpdated: new Date().toISOString(), }; - }); afterEach(() => { // Clear all mocks - jest.clearAllMocks(); + jest.resetAllMocks(); }); test('should save and return the correct value', () => { diff --git a/test-with-jest/src/mock-function/data/connect-to-cosmos.ts b/test-with-jest/src/mock-function/data/connect-to-cosmos.ts index bc9d9108..154ef2a3 100644 --- a/test-with-jest/src/mock-function/data/connect-to-cosmos.ts +++ b/test-with-jest/src/mock-function/data/connect-to-cosmos.ts @@ -3,7 +3,7 @@ import { Container, CosmosClient } from '@azure/cosmos'; import { DefaultAzureCredential } from '@azure/identity'; import 'dotenv/config'; -import { v4 as uuidv4 } from 'uuid'; +import { randomUUID } from 'crypto'; export { Container }; @@ -32,5 +32,5 @@ export async function connectToContainer(): Promise { return container; } export function getUniqueId(): string { - return uuidv4(); + return randomUUID(); } diff --git a/test-with-jest/src/mock-function/data/fake-data.ts b/test-with-jest/src/mock-function/data/fake-data.ts index 326912df..d30b44ae 100644 --- a/test-with-jest/src/mock-function/data/fake-data.ts +++ b/test-with-jest/src/mock-function/data/fake-data.ts @@ -1,18 +1,26 @@ -import { v4 as uuidv4 } from 'uuid'; -import { faker } from '@faker-js/faker'; import { DbDocument, RawInput } from './model'; +import { randomUUID } from 'crypto'; + +// Predefined arrays of first and last names +const firstNames = ['Alice', 'Bob', 'Charlie', 'Dana', 'Eve', 'Frank']; +const lastNames = ['Smith', 'Johnson', 'Williams', 'Brown', 'Jones', 'Miller']; + +function getRandomElement(arr: T[]): T { + const randomIndex = Math.floor(Math.random() * arr.length); + return arr[randomIndex]; +} function createFixture(): T { const result = { - first: faker.person.firstName(), - last: faker.person.lastName(), + first: getRandomElement(firstNames), + last: getRandomElement(lastNames), }; return result as T; } export function createTestInput(): RawInput { const { first, last } = createFixture(); - return { id: uuidv4(), first, last }; + return { id: randomUUID(), first, last }; } export function createTestInputAndResult(): { diff --git a/test-with-jest/src/mock-function/index.ts b/test-with-jest/src/mock-function/index.ts index b4eac299..56f811f1 100644 --- a/test-with-jest/src/mock-function/index.ts +++ b/test-with-jest/src/mock-function/index.ts @@ -1,14 +1,19 @@ import { connectToContainer } from './data/connect-to-cosmos'; import { createTestInput } from './data/fake-data'; -import { DbDocument, DbError, VerificationErrors } from './data/model'; import { insertDocument } from './lib/insert'; +import { DbDocument, DbError, VerificationErrors } from './data/model'; + +async function main(): Promise { + try { + const container = await connectToContainer(); + const input = createTestInput(); + return insertDocument(container, input); + } catch (error) { + console.error(error); + process.exit(1); + } +} -try { - const container = await connectToContainer(); - const input = createTestInput(); - const result = await insertDocument(container, input); - console.log(result); -} catch (error) { - console.error(error); - process.exit(1); -} \ No newline at end of file +main() + .then((result: any) => console.log(result)) + .catch(console.error); diff --git a/test-with-jest/src/mock-function/lib/insert.spec.ts b/test-with-jest/src/mock-function/lib/insert.spec.ts index 5082e864..55a17446 100644 --- a/test-with-jest/src/mock-function/lib/insert.spec.ts +++ b/test-with-jest/src/mock-function/lib/insert.spec.ts @@ -1,15 +1,8 @@ // insertDocument.test.ts import { Container } from '../data/connect-to-cosmos'; import { createTestInputAndResult } from '../data/fake-data'; -import type { - DbDocument, - DbError, - RawInput -} from '../data/model'; -import { - isDbError, - isVerificationErrors, -} from '../data/model'; +import type { DbDocument, DbError, RawInput } from '../data/model'; +import { isDbError, isVerificationErrors } from '../data/model'; import { inputVerified } from '../data/verify'; import { insertDocument } from './insert'; @@ -29,7 +22,7 @@ describe('SDK', () => { beforeEach(() => { // Clear all mocks before each test - jest.clearAllMocks(); + jest.resetAllMocks(); // Mock the Cosmos DB Container create method mockContainer = { diff --git a/test-with-jest/src/test-boilerplate/boilerplate-with-mock.spec.ts b/test-with-jest/src/test-boilerplate/boilerplate-with-mock.spec.ts index 20c90818..e50b6d9d 100644 --- a/test-with-jest/src/test-boilerplate/boilerplate-with-mock.spec.ts +++ b/test-with-jest/src/test-boilerplate/boilerplate-with-mock.spec.ts @@ -8,7 +8,7 @@ jest.mock('../mock-function/data/connect-to-cosmos', () => ({ describe('nameOfGroupOfTests', () => { beforeEach(() => { // Clear all mocks before each test - jest.clearAllMocks(); + jest.resetAllMocks(); // Other setup required before each test }); diff --git a/test-with-node-testrunner/.eslintrc.json b/test-with-node-testrunner/.eslintrc.json new file mode 100644 index 00000000..55e8766c --- /dev/null +++ b/test-with-node-testrunner/.eslintrc.json @@ -0,0 +1,29 @@ +{ + "env": { + "node": true + }, + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:@typescript-eslint/recommended-requiring-type-checking" + ], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": 2021, + "project": "./tsconfig.json" + }, + "plugins": [ + "jest", + "@typescript-eslint" + ], + "rules": { + "no-console": "off", + "no-noImplicitAny": "off", + "require-await": "error", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-unsafe-assignment": "off", + "@typescript-eslint/no-unsafe-argument": "off", + "@typescript-eslint/no-unsafe-member-access": "off", + "@typescript-eslint/unbound-method": "off" + } +} \ No newline at end of file diff --git a/test-with-node-testrunner/package.json b/test-with-node-testrunner/package.json index 5e9e395d..41cfd5ff 100644 --- a/test-with-node-testrunner/package.json +++ b/test-with-node-testrunner/package.json @@ -4,17 +4,25 @@ "main": "index.js", "type": "module", "scripts": { - "build": "rm -rf dist && tsc", + "prebuild": "npm run format && npm run lint", + "build": "tsc", + "lint": "eslint \"./src/**/*.ts\" --fix", + "format": "prettier --write './**/*.{ts,tsx}", "test": "npm run build && node --test --experimental-test-coverage --experimental-test-module-mocks --trace-exit" }, "devDependencies": { - "@faker-js/faker": "^8.4.1", - "@types/node": "^22.13.10" + "@types/node": "^22.13.10", + "@typescript-eslint/eslint-plugin": "^8.2.0", + "eslint-config-airbnb-base": "^15.0.0", + "eslint-plugin-import": "^2.29.1", + "eslint-plugin-jest": "^28.8.0", + "eslint": "^8.56.0", + "prettier": "^3.2.4", + "typescript": "^5.5.4" }, "dependencies": { "@azure/cosmos": "^4.1.0", "@azure/identity": "^4.4.1", - "dotenv": "^16.4.5", - "uuid": "^10.0.0" + "dotenv": "^16.4.5" } } diff --git a/test-with-node-testrunner/src/data/connect-to-cosmos.ts b/test-with-node-testrunner/src/data/connect-to-cosmos.ts index bd0b9566..840c43ec 100644 --- a/test-with-node-testrunner/src/data/connect-to-cosmos.ts +++ b/test-with-node-testrunner/src/data/connect-to-cosmos.ts @@ -3,7 +3,7 @@ import { Container, CosmosClient } from '@azure/cosmos'; import { DefaultAzureCredential } from '@azure/identity'; import 'dotenv/config'; -import { v4 as uuidv4 } from 'uuid'; +import { randomUUID } from 'crypto'; export { Container }; @@ -34,6 +34,6 @@ export default class CosmosConnector { } static getUniqueId(): string { - return uuidv4(); + return randomUUID(); } } diff --git a/test-with-node-testrunner/src/data/fake-data.ts b/test-with-node-testrunner/src/data/fake-data.ts index 82c90b66..6e77a8c1 100644 --- a/test-with-node-testrunner/src/data/fake-data.ts +++ b/test-with-node-testrunner/src/data/fake-data.ts @@ -1,18 +1,26 @@ -import { v4 as uuidv4 } from 'uuid'; -import { faker } from '@faker-js/faker'; import type { DbDocument, RawInput } from './model.js'; +import { randomUUID } from 'crypto'; + +// Predefined arrays of first and last names +const firstNames = ['Alice', 'Bob', 'Charlie', 'Dana', 'Eve', 'Frank']; +const lastNames = ['Smith', 'Johnson', 'Williams', 'Brown', 'Jones', 'Miller']; + +function getRandomElement(arr: T[]): T { + const randomIndex = Math.floor(Math.random() * arr.length); + return arr[randomIndex]; +} function createFixture(): T { const result = { - first: faker.person.firstName(), - last: faker.person.lastName(), + first: getRandomElement(firstNames), + last: getRandomElement(lastNames), }; return result as T; } export function createTestInput(): RawInput { const { first, last } = createFixture(); - return { id: uuidv4(), first, last }; + return { id: randomUUID(), first, last }; } export function createTestInputAndResult(): { diff --git a/test-with-node-testrunner/src/index.ts b/test-with-node-testrunner/src/index.ts index 1370fa14..caa628be 100644 --- a/test-with-node-testrunner/src/index.ts +++ b/test-with-node-testrunner/src/index.ts @@ -7,8 +7,7 @@ try { const input = createTestInput(); const result = await insertDocument(container, input); console.log(result); -} -catch (error) { +} catch (error) { console.error(error); process.exit(1); -} \ No newline at end of file +} diff --git a/test-with-node-testrunner/src/lib/insert.ts b/test-with-node-testrunner/src/lib/insert.ts index e873cadd..0dd710a6 100644 --- a/test-with-node-testrunner/src/lib/insert.ts +++ b/test-with-node-testrunner/src/lib/insert.ts @@ -1,10 +1,10 @@ // insertDocument.ts import { Container } from '../data/connect-to-cosmos.js'; -import type { +import type { DbDocument, - DbError, + DbError, RawInput, - VerificationErrors, + VerificationErrors, } from '../data/model.js'; import Verify from '../data/verify.js'; diff --git a/test-with-node-testrunner/test/01-spies.test.ts b/test-with-node-testrunner/test/01-spies.test.ts index 02da8fc7..c9c787c5 100644 --- a/test-with-node-testrunner/test/01-spies.test.ts +++ b/test-with-node-testrunner/test/01-spies.test.ts @@ -1,33 +1,26 @@ -import { - describe, - it, - mock -} from 'node:test'; +import { describe, it, mock } from 'node:test'; import assert from 'node:assert'; - -function run({fn, times}){ - for(let i = 0; i < times; i++){ - fn({current: i * 5}); - } +function run({ fn, times }) { + for (let i = 0; i < times; i++) { + fn({ current: i * 5 }); + } } describe('Spies', () => { - it('should verify calls in a mock', () => { - const spy = mock.fn(); - spy.mock.mockImplementation((arg) => { - console.log(arg); - }); - - - run({fn: spy, times: 3}); + it('should verify calls in a mock', () => { + const spy = mock.fn(); + spy.mock.mockImplementation((arg) => { + console.log(arg); + }); - assert.strictEqual(spy.mock.callCount(), 3); - const calls = spy.mock.calls; + run({ fn: spy, times: 3 }); - assert.deepStrictEqual(calls[0].arguments[0], {current: 0}); - assert.deepStrictEqual(calls[1].arguments[0], {current: 5}); - assert.deepStrictEqual(calls[2].arguments[0], {current: 10}); + assert.strictEqual(spy.mock.callCount(), 3); + const calls = spy.mock.calls; - }); -}); \ No newline at end of file + assert.deepStrictEqual(calls[0].arguments[0], { current: 0 }); + assert.deepStrictEqual(calls[1].arguments[0], { current: 5 }); + assert.deepStrictEqual(calls[2].arguments[0], { current: 10 }); + }); +}); diff --git a/test-with-node-testrunner/test/02-stubs.test.ts b/test-with-node-testrunner/test/02-stubs.test.ts index 883920a4..253af121 100644 --- a/test-with-node-testrunner/test/02-stubs.test.ts +++ b/test-with-node-testrunner/test/02-stubs.test.ts @@ -30,8 +30,8 @@ describe('Stub Test Suite', () => { m.mockImplementation(async () => [ { id: '1', - title: 'Sample Post' - } + title: 'Sample Post', + }, ]); const result = await run({ limit: 1 }); @@ -45,16 +45,15 @@ describe('Stub Test Suite', () => { }); it('should stub different values for API calls', async () => { - // Instead of chaining mockImplementationOnce, build a responses array. const m = mock.method(Service, 'getTalks').mock; const responses = [ async () => [{ id: '1', title: 'Post One' }], async () => [{ id: '2', title: 'Post Two' }], - async () => [{ id: '3', title: 'Post Three' }] + async () => [{ id: '3', title: 'Post Three' }], ]; - m.mockImplementation(async (args) => { + m.mockImplementation(async (_) => { // Return the next response from the array. const fn = responses.shift(); return fn ? fn() : []; diff --git a/test-with-node-testrunner/test/boilerplate-with-mock.test.ts b/test-with-node-testrunner/test/boilerplate-with-mock.test.ts index 2b6e8755..bff82869 100644 --- a/test-with-node-testrunner/test/boilerplate-with-mock.test.ts +++ b/test-with-node-testrunner/test/boilerplate-with-mock.test.ts @@ -1,45 +1,34 @@ // boilerplate-with-mock.test.ts -import { - describe, - it, - afterEach, - beforeEach, - mock - } from 'node:test' - import assert from 'node:assert' - +import { describe, it, afterEach, beforeEach, mock } from 'node:test'; +import assert from 'node:assert'; // original value is 1 const result = 1; class MyService { - static async myFunction() { - return Promise.resolve(result); - } + static async myFunction() { + return Promise.resolve(result); + } } describe('boilerplate with mock', () => { - beforeEach(() =>{ - // Setup required before each test - mock.restoreAll() - }) - afterEach(() => { - // Cleanup required after each test - }); - - it('should if ', async () => { - - // Arrange: Replace the original implementation with a mock, returning 2 - const m = mock.method(MyService, "myFunction").mock; - m.mockImplementation(async () => Promise.resolve(2)) - - - // Act: Test the function, but get the mocked value - const result = await MyService.myFunction(); - - // Assert - assert.strictEqual(result, - 2 - ) - }) - }) \ No newline at end of file + beforeEach(() => { + // Setup required before each test + mock.restoreAll(); + }); + afterEach(() => { + // Cleanup required after each test + }); + + it('should if ', async () => { + // Arrange: Replace the original implementation with a mock, returning 2 + const m = mock.method(MyService, 'myFunction').mock; + m.mockImplementation(async () => Promise.resolve(2)); + + // Act: Test the function, but get the mocked value + const result = await MyService.myFunction(); + + // Assert + assert.strictEqual(result, 2); + }); +}); diff --git a/test-with-node-testrunner/test/boilerplate.test.ts b/test-with-node-testrunner/test/boilerplate.test.ts index ad6e054d..182baf5b 100644 --- a/test-with-node-testrunner/test/boilerplate.test.ts +++ b/test-with-node-testrunner/test/boilerplate.test.ts @@ -1,21 +1,15 @@ -import { - describe, - it, - afterEach, - beforeEach, - mock - } from 'node:test' - import assert from 'node:assert' +import { describe, it, afterEach, beforeEach, mock } from 'node:test'; +import assert from 'node:assert'; describe('boilerplate', () => { - beforeEach(() =>{ - // Setup required before each test - }) - afterEach(() => { - // Cleanup required after each test - }); - - it('should if ', async () => { + beforeEach(() => { + // Setup required before each test + }); + afterEach(() => { + // Cleanup required after each test + }); + + it('should if ', async () => { // Arrange // - set up the test data and the expected result // Act @@ -23,6 +17,5 @@ describe('boilerplate', () => { // Assert // - check the state: result returned from function // - check the behavior: dependency function calls - - }) - }) \ No newline at end of file + }); +}); diff --git a/test-with-node-testrunner/test/fake-in-mem-db.test.ts b/test-with-node-testrunner/test/fake-in-mem-db.test.ts index e986dbce..fb86b061 100644 --- a/test-with-node-testrunner/test/fake-in-mem-db.test.ts +++ b/test-with-node-testrunner/test/fake-in-mem-db.test.ts @@ -62,4 +62,4 @@ describe('In-Mem DB', () => { const calls = saveSpy.calls; assert.deepStrictEqual(calls[0].arguments, [testKey, testValue]); }); -}); \ No newline at end of file +}); diff --git a/test-with-node-testrunner/test/insert.test.ts b/test-with-node-testrunner/test/insert.test.ts index 20771eb4..ba3b1bd3 100644 --- a/test-with-node-testrunner/test/insert.test.ts +++ b/test-with-node-testrunner/test/insert.test.ts @@ -4,30 +4,20 @@ import assert from 'node:assert'; import { Container } from '../src/data/connect-to-cosmos.js'; import { createTestInputAndResult } from '../src/data/fake-data.js'; -import type { - DbDocument, - DbError, - RawInput, -} from '../src/data/model.js'; -import { - isDbError, - isVerificationErrors, -} from '../src/data/model.js'; +import type { DbDocument, DbError, RawInput } from '../src/data/model.js'; +import { isDbError, isVerificationErrors } from '../src/data/model.js'; import Verify from '../src/data/verify.js'; import CosmosConnector from '../src/data/connect-to-cosmos.js'; import { insertDocument } from '../src/lib/insert.js'; - describe('SDK', () => { - beforeEach(() => { // Clear all mocks before each test - mock.restoreAll() - }) + mock.restoreAll(); + }); it('should return verification error if input is not verified', async () => { - const fakeContainer = { items: { create: async (_: any) => { @@ -36,13 +26,13 @@ describe('SDK', () => { }, } as unknown as Container; - const mVerify = mock.method(Verify, "inputVerified").mock; + const mVerify = mock.method(Verify, 'inputVerified').mock; mVerify.mockImplementation(() => false); - const mGetUniqueId = mock.method(CosmosConnector, "getUniqueId").mock; + const mGetUniqueId = mock.method(CosmosConnector, 'getUniqueId').mock; mGetUniqueId.mockImplementation(() => 'unique-id'); - const mContainerCreate = mock.method(fakeContainer.items, "create").mock; + const mContainerCreate = mock.method(fakeContainer.items, 'create').mock; // Arrange: wrong shape of document on purpose. const doc = { name: 'test' } as unknown as RawInput; @@ -52,18 +42,20 @@ describe('SDK', () => { // Assert - State verification. if (isVerificationErrors(insertDocumentResult)) { - assert.deepStrictEqual(insertDocumentResult, { message: 'Verification failed' }); + assert.deepStrictEqual(insertDocumentResult, { + message: 'Verification failed', + }); } else { throw new Error('Result is not of type VerificationErrors'); } // Assert - Behavior verification: Verify that create was never called. assert.strictEqual(mContainerCreate.callCount(), 0); - }); it('should insert document successfully', async () => { // Arrange: override inputVerified to return true. - const { input, result }: { input: RawInput; result: Partial } = createTestInputAndResult(); + const { input, result }: { input: RawInput; result: Partial } = + createTestInputAndResult(); const fakeContainer = { items: { @@ -73,10 +65,13 @@ describe('SDK', () => { }, } as unknown as Container; - const mVerify = mock.method(Verify, "inputVerified").mock; + const mVerify = mock.method(Verify, 'inputVerified').mock; mVerify.mockImplementation(() => true); - const mContainerCreate = mock.method(fakeContainer.items as any, "create").mock; + const mContainerCreate = mock.method( + fakeContainer.items as any, + 'create', + ).mock; mContainerCreate.mockImplementation(async (doc: any) => { return { resource: result }; }); @@ -97,7 +92,7 @@ describe('SDK', () => { it('should return error if db insert fails', async () => { // Arrange: override inputVerified to return true. const { input, result } = createTestInputAndResult(); - let errorMessage: string = 'An unknown error occurred'; + const errorMessage: string = 'An unknown error occurred'; const fakeContainer = { items: { @@ -107,10 +102,10 @@ describe('SDK', () => { }, } as unknown as Container; - const mVerify = mock.method(Verify, "inputVerified").mock; + const mVerify = mock.method(Verify, 'inputVerified').mock; mVerify.mockImplementation(() => true); - const mContainerCreate = mock.method(fakeContainer.items, "create").mock; + const mContainerCreate = mock.method(fakeContainer.items, 'create').mock; mContainerCreate.mockImplementation(async (doc: any) => { const mockError: DbError = { message: errorMessage, @@ -130,4 +125,4 @@ describe('SDK', () => { name: result.name, }); }); -}); \ No newline at end of file +}); diff --git a/test-with-vitest/.eslintrc.json b/test-with-vitest/.eslintrc.json new file mode 100644 index 00000000..55e8766c --- /dev/null +++ b/test-with-vitest/.eslintrc.json @@ -0,0 +1,29 @@ +{ + "env": { + "node": true + }, + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:@typescript-eslint/recommended-requiring-type-checking" + ], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": 2021, + "project": "./tsconfig.json" + }, + "plugins": [ + "jest", + "@typescript-eslint" + ], + "rules": { + "no-console": "off", + "no-noImplicitAny": "off", + "require-await": "error", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-unsafe-assignment": "off", + "@typescript-eslint/no-unsafe-argument": "off", + "@typescript-eslint/no-unsafe-member-access": "off", + "@typescript-eslint/unbound-method": "off" + } +} \ No newline at end of file diff --git a/test-with-vitest/package.json b/test-with-vitest/package.json index 0294231d..9b7a690f 100644 --- a/test-with-vitest/package.json +++ b/test-with-vitest/package.json @@ -4,23 +4,32 @@ "main": "index.js", "type": "module", "scripts": { + "prebuild": "npm run format && npm run lint", "build": "rm -rf dist && tsc", "test": "vitest run --coverage", - "test2": "node --trace-exit ./node_modules/vitest/vitest.js run --coverage" + "test2": "node --trace-exit ./node_modules/vitest/vitest.js run --coverage", + "lint": "eslint \"./src/**/*.ts\" --fix", + "format": "prettier --write './**/*.{ts,tsx}" }, "keywords": [], "author": "", "license": "ISC", "description": "", "devDependencies": { - "@faker-js/faker": "^8.4.1", + "@types/node": "^22.13.10", + "@typescript-eslint/eslint-plugin": "^8.2.0", "@vitest/coverage-istanbul": "^3.0.8", + "eslint-config-airbnb-base": "^15.0.0", + "eslint-plugin-import": "^2.29.1", + "eslint-plugin-jest": "^28.8.0", + "eslint": "^8.56.0", + "prettier": "^3.2.4", + "typescript": "^5.5.4", "vitest": "^3.0.8" }, "dependencies": { "@azure/cosmos": "^4.1.0", "@azure/identity": "^4.4.1", - "dotenv": "^16.4.5", - "uuid": "^10.0.0" + "dotenv": "^16.4.5" } } diff --git a/test-with-vitest/src/data/connect-to-cosmos.ts b/test-with-vitest/src/data/connect-to-cosmos.ts index bd0b9566..840c43ec 100644 --- a/test-with-vitest/src/data/connect-to-cosmos.ts +++ b/test-with-vitest/src/data/connect-to-cosmos.ts @@ -3,7 +3,7 @@ import { Container, CosmosClient } from '@azure/cosmos'; import { DefaultAzureCredential } from '@azure/identity'; import 'dotenv/config'; -import { v4 as uuidv4 } from 'uuid'; +import { randomUUID } from 'crypto'; export { Container }; @@ -34,6 +34,6 @@ export default class CosmosConnector { } static getUniqueId(): string { - return uuidv4(); + return randomUUID(); } } diff --git a/test-with-vitest/src/data/fake-data.ts b/test-with-vitest/src/data/fake-data.ts index 82c90b66..6e77a8c1 100644 --- a/test-with-vitest/src/data/fake-data.ts +++ b/test-with-vitest/src/data/fake-data.ts @@ -1,18 +1,26 @@ -import { v4 as uuidv4 } from 'uuid'; -import { faker } from '@faker-js/faker'; import type { DbDocument, RawInput } from './model.js'; +import { randomUUID } from 'crypto'; + +// Predefined arrays of first and last names +const firstNames = ['Alice', 'Bob', 'Charlie', 'Dana', 'Eve', 'Frank']; +const lastNames = ['Smith', 'Johnson', 'Williams', 'Brown', 'Jones', 'Miller']; + +function getRandomElement(arr: T[]): T { + const randomIndex = Math.floor(Math.random() * arr.length); + return arr[randomIndex]; +} function createFixture(): T { const result = { - first: faker.person.firstName(), - last: faker.person.lastName(), + first: getRandomElement(firstNames), + last: getRandomElement(lastNames), }; return result as T; } export function createTestInput(): RawInput { const { first, last } = createFixture(); - return { id: uuidv4(), first, last }; + return { id: randomUUID(), first, last }; } export function createTestInputAndResult(): { diff --git a/test-with-vitest/src/index.ts b/test-with-vitest/src/index.ts index 68dcfa7e..1d68dbc5 100644 --- a/test-with-vitest/src/index.ts +++ b/test-with-vitest/src/index.ts @@ -2,12 +2,11 @@ import CosmosConnector from './data/connect-to-cosmos.js'; import { createTestInput } from './data/fake-data.js'; import { insertDocument } from './lib/insert.js'; try { - const container = await CosmosConnector.connectToContainer(); - const input = createTestInput(); - const result = await insertDocument(container, input); - console.log(result); -} -catch (error) { - console.error(error); - process.exit(1); + const container = await CosmosConnector.connectToContainer(); + const input = createTestInput(); + const result = await insertDocument(container, input); + console.log(result); +} catch (error) { + console.error(error); + process.exit(1); } diff --git a/test-with-vitest/src/lib/insert.ts b/test-with-vitest/src/lib/insert.ts index e873cadd..0dd710a6 100644 --- a/test-with-vitest/src/lib/insert.ts +++ b/test-with-vitest/src/lib/insert.ts @@ -1,10 +1,10 @@ // insertDocument.ts import { Container } from '../data/connect-to-cosmos.js'; -import type { +import type { DbDocument, - DbError, + DbError, RawInput, - VerificationErrors, + VerificationErrors, } from '../data/model.js'; import Verify from '../data/verify.js'; diff --git a/test-with-vitest/tests/01-spies.test.ts b/test-with-vitest/tests/01-spies.test.ts index 76a946ee..9278a984 100644 --- a/test-with-vitest/tests/01-spies.test.ts +++ b/test-with-vitest/tests/01-spies.test.ts @@ -1,18 +1,23 @@ import { it, expect, vi } from 'vitest'; -function run({ fn, times }: { fn: (arg: { current: number }) => void; times: number }) { +function run({ + fn, + times, +}: { + fn: (arg: { current: number }) => void; + times: number; +}) { for (let i = 0; i < times; i++) { fn({ current: i * 5 }); } } +it('Spies: should verify calls in a mock', () => { + const spy = vi.fn(); + run({ fn: spy, times: 3 }); - it('Spies: should verify calls in a mock', () => { - const spy = vi.fn(); - run({ fn: spy, times: 3 }); - - expect(spy).toHaveBeenCalledTimes(3); - expect(spy.mock.calls[0][0]).toEqual({ current: 0 }); - expect(spy.mock.calls[1][0]).toEqual({ current: 5 }); - expect(spy.mock.calls[2][0]).toEqual({ current: 10 }); - }); + expect(spy).toHaveBeenCalledTimes(3); + expect(spy.mock.calls[0][0]).toEqual({ current: 0 }); + expect(spy.mock.calls[1][0]).toEqual({ current: 5 }); + expect(spy.mock.calls[2][0]).toEqual({ current: 10 }); +}); diff --git a/test-with-vitest/tests/02-stubs.test.ts b/test-with-vitest/tests/02-stubs.test.ts index 9b5f4fb7..470ffe76 100644 --- a/test-with-vitest/tests/02-stubs.test.ts +++ b/test-with-vitest/tests/02-stubs.test.ts @@ -1,7 +1,13 @@ import { describe, it, beforeEach, afterEach, expect, vi } from 'vitest'; class Service { - static async getTalks({ skip, limit }: { skip: number; limit: number }): Promise { + static async getTalks({ + skip, + limit, + }: { + skip: number; + limit: number; + }): Promise { // Use a public API (jsonplaceholder) to retrieve posts with pagination. const url = `https://jsonplaceholder.typicode.com/posts?_start=${skip}&_limit=${limit}`; const response = await fetch(url); @@ -35,8 +41,8 @@ describe('Stub Test Suite', () => { m.mockImplementation(async () => [ { id: '1', - title: 'Sample Post' - } + title: 'Sample Post', + }, ]); const result = await run({ limit: 1 }); @@ -54,7 +60,7 @@ describe('Stub Test Suite', () => { const responses = [ async () => [{ id: '1', title: 'Post One' }], async () => [{ id: '2', title: 'Post Two' }], - async () => [{ id: '3', title: 'Post Three' }] + async () => [{ id: '3', title: 'Post Three' }], ]; m.mockImplementation(async (args) => { @@ -85,4 +91,4 @@ describe('Stub Test Suite', () => { expect(m.mock.calls[1][0]).toEqual({ skip: 1, limit: 1 }); expect(m.mock.calls[2][0]).toEqual({ skip: 2, limit: 1 }); }); -}); \ No newline at end of file +}); diff --git a/test-with-vitest/tests/boilerplate-with-mock.test.ts b/test-with-vitest/tests/boilerplate-with-mock.test.ts index c23839c5..313539f7 100644 --- a/test-with-vitest/tests/boilerplate-with-mock.test.ts +++ b/test-with-vitest/tests/boilerplate-with-mock.test.ts @@ -29,4 +29,4 @@ describe('boilerplate with mock', () => { // Assert expect(resultVal).toBe(2); }); -}); \ No newline at end of file +}); diff --git a/test-with-vitest/tests/boilerplate.test.ts b/test-with-vitest/tests/boilerplate.test.ts index eae12f97..af00c59f 100644 --- a/test-with-vitest/tests/boilerplate.test.ts +++ b/test-with-vitest/tests/boilerplate.test.ts @@ -12,12 +12,10 @@ describe('boilerplate', () => { it('should if ', async () => { // Arrange // - set up the test data and the expected result - // Act // - call the function to test - // Assert // - check the state: result returned from function // - check the behavior: dependency function calls }); -}); \ No newline at end of file +}); diff --git a/test-with-vitest/tests/fake-in-mem-db.test.ts b/test-with-vitest/tests/fake-in-mem-db.test.ts index 4f89cef8..00973957 100644 --- a/test-with-vitest/tests/fake-in-mem-db.test.ts +++ b/test-with-vitest/tests/fake-in-mem-db.test.ts @@ -60,4 +60,4 @@ describe('In-Mem DB', () => { expect(saveSpy).toHaveBeenCalledTimes(1); expect(saveSpy).toHaveBeenCalledWith(testKey, testValue); }); -}); \ No newline at end of file +}); diff --git a/test-with-vitest/tests/insert.test.ts b/test-with-vitest/tests/insert.test.ts index 73f290cd..42d81bd1 100644 --- a/test-with-vitest/tests/insert.test.ts +++ b/test-with-vitest/tests/insert.test.ts @@ -4,18 +4,10 @@ import type { Container, ItemResponse } from '@azure/cosmos'; import { insertDocument } from '../src/lib/insert.js'; import { createTestInputAndResult } from '../src/data/fake-data.js'; -import type { - DbDocument, - DbError, - RawInput -} from '../src/data/model.js'; -import { - isDbError, - isVerificationErrors, -} from '../src/data/model.js'; +import type { DbDocument, DbError, RawInput } from '../src/data/model.js'; +import { isDbError, isVerificationErrors } from '../src/data/model.js'; import Verify from '../src/data/verify.js'; - describe('insertDocument', () => { let fakeContainer: Container; @@ -67,7 +59,9 @@ describe('insertDocument', () => { // Set up the mocked return value. // Here we "cast" our minimal object to satisfy the expected type ItemResponse. - (fakeContainer.items.create as unknown as ReturnType).mockResolvedValue({ + ( + fakeContainer.items.create as unknown as ReturnType + ).mockResolvedValue({ resource: result, // Minimal additional properties required by ItemResponse. item: result, @@ -88,14 +82,13 @@ describe('insertDocument', () => { expect(inputVerifiedMock).toHaveBeenCalledTimes(1); expect(fakeContainer.items.create).toHaveBeenCalledTimes(1); expect( - (fakeContainer.items.create as unknown as ReturnType).mock.calls[0][0] + (fakeContainer.items.create as unknown as ReturnType).mock + .calls[0][0], ).toEqual({ id: input.id, name: result.name, }); }); - - it('should return error if db insert fails', async () => { // Arrange – create input and expected result data. @@ -111,7 +104,9 @@ describe('insertDocument', () => { code: 500, }; - (fakeContainer.items.create as unknown as ReturnType).mockRejectedValue(mockError as unknown as DbError); + ( + fakeContainer.items.create as unknown as ReturnType + ).mockRejectedValue(mockError as unknown as DbError); // Act – call the function under test. const insertDocumentResult = await insertDocument(fakeContainer, input); @@ -127,12 +122,11 @@ describe('insertDocument', () => { expect(inputVerifiedMock).toHaveBeenCalledTimes(1); expect(fakeContainer.items.create).toHaveBeenCalledTimes(1); expect( - (fakeContainer.items.create as unknown as ReturnType).mock.calls[0][0] + (fakeContainer.items.create as unknown as ReturnType).mock + .calls[0][0], ).toEqual({ id: input.id, name: result.name, }); }); - - -}); \ No newline at end of file +}); diff --git a/test-with-vitest/vitest.config.ts b/test-with-vitest/vitest.config.ts index c9ae13c3..1009b7a4 100644 --- a/test-with-vitest/vitest.config.ts +++ b/test-with-vitest/vitest.config.ts @@ -14,4 +14,4 @@ export default defineConfig({ // statements: 80, }, }, -}); \ No newline at end of file +}); diff --git a/unit-testing/.editorconfig b/unit-testing/.editorconfig deleted file mode 100644 index 5688c0d1..00000000 --- a/unit-testing/.editorconfig +++ /dev/null @@ -1,10 +0,0 @@ -root = true - -[*] -indent_style = space -indent_size = 2 -end_of_line = auto -charset = utf-8 -trim_trailing_whitespace = true -insert_final_newline = true -quote_type = single \ No newline at end of file