summaryrefslogtreecommitdiff
path: root/gui
diff options
context:
space:
mode:
Diffstat (limited to 'gui')
-rw-r--r--gui/package-lock.json901
-rw-r--r--gui/package.json8
-rw-r--r--gui/src/ajax/backend.ts57
-rw-r--r--gui/src/components/app/App.tsx70
-rw-r--r--gui/src/components/histogram/Histogram.tsx44
-rw-r--r--gui/src/types/BalanceToBudgetRatio.spec.ts307
-rw-r--r--gui/src/types/account.ts26
-rw-r--r--gui/src/types/balanceMap.ts17
-rw-r--r--gui/src/types/balanceToBudgetRatio.ts83
-rw-r--r--gui/src/types/budget.ts12
-rw-r--r--gui/src/types/budgetMap.ts16
-rw-r--r--gui/src/types/category.ts51
-rw-r--r--gui/src/types/currency/gbp.ts10
-rw-r--r--gui/src/types/dtos/accountDto.ts8
-rw-r--r--gui/yarn.lock690
15 files changed, 1511 insertions, 789 deletions
diff --git a/gui/package-lock.json b/gui/package-lock.json
index 6ecf2bc..84127a9 100644
--- a/gui/package-lock.json
+++ b/gui/package-lock.json
@@ -12,10 +12,12 @@
"solid-js": "^1.8.20"
},
"devDependencies": {
+ "jsdom": "^25.0.0",
"sass-embedded": "^1.78.0",
"typescript": "^5.5.3",
"vite": "^5.4.1",
- "vite-plugin-solid": "^2.10.2"
+ "vite-plugin-solid": "^2.10.2",
+ "vitest": "^2.1.1"
}
},
"node_modules/@ampproject/remapping": {
@@ -475,6 +477,145 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/@types/node": {
+ "version": "22.5.5",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.5.tgz",
+ "integrity": "sha512-Xjs4y5UPO/CLdzpgR6GirZJx36yScjh73+2NlLlkFRSoQN8B0DpfXPdZGnvVmLRLOsqDpOfTNv7D9trgGhmOIA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "undici-types": "~6.19.2"
+ }
+ },
+ "node_modules/@vitest/expect": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.1.tgz",
+ "integrity": "sha512-YeueunS0HiHiQxk+KEOnq/QMzlUuOzbU1Go+PgAsHvvv3tUkJPm9xWt+6ITNTlzsMXUjmgm5T+U7KBPK2qQV6w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/spy": "2.1.1",
+ "@vitest/utils": "2.1.1",
+ "chai": "^5.1.1",
+ "tinyrainbow": "^1.2.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/mocker": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.1.tgz",
+ "integrity": "sha512-LNN5VwOEdJqCmJ/2XJBywB11DLlkbY0ooDJW3uRX5cZyYCrc4PI/ePX0iQhE3BiEGiQmK4GE7Q/PqCkkaiPnrA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/spy": "^2.1.0-beta.1",
+ "estree-walker": "^3.0.3",
+ "magic-string": "^0.30.11"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ },
+ "peerDependencies": {
+ "@vitest/spy": "2.1.1",
+ "msw": "^2.3.5",
+ "vite": "^5.0.0"
+ },
+ "peerDependenciesMeta": {
+ "msw": {
+ "optional": true
+ },
+ "vite": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@vitest/pretty-format": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.1.tgz",
+ "integrity": "sha512-SjxPFOtuINDUW8/UkElJYQSFtnWX7tMksSGW0vfjxMneFqxVr8YJ979QpMbDW7g+BIiq88RAGDjf7en6rvLPPQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tinyrainbow": "^1.2.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/runner": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.1.tgz",
+ "integrity": "sha512-uTPuY6PWOYitIkLPidaY5L3t0JJITdGTSwBtwMjKzo5O6RCOEncz9PUN+0pDidX8kTHYjO0EwUIvhlGpnGpxmA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/utils": "2.1.1",
+ "pathe": "^1.1.2"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/snapshot": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.1.tgz",
+ "integrity": "sha512-BnSku1WFy7r4mm96ha2FzN99AZJgpZOWrAhtQfoxjUU5YMRpq1zmHRq7a5K9/NjqonebO7iVDla+VvZS8BOWMw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/pretty-format": "2.1.1",
+ "magic-string": "^0.30.11",
+ "pathe": "^1.1.2"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/spy": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.1.tgz",
+ "integrity": "sha512-ZM39BnZ9t/xZ/nF4UwRH5il0Sw93QnZXd9NAZGRpIgj0yvVwPpLd702s/Cx955rGaMlyBQkZJ2Ir7qyY48VZ+g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tinyspy": "^3.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/utils": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.1.tgz",
+ "integrity": "sha512-Y6Q9TsI+qJ2CC0ZKj6VBb+T8UPz593N113nnUykqwANqhgf3QkZeHFlusgKLTqrnVHbj/XDKZcDHol+dxVT+rQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/pretty-format": "2.1.1",
+ "loupe": "^3.1.1",
+ "tinyrainbow": "^1.2.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/agent-base": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz",
+ "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
"node_modules/ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
@@ -488,6 +629,23 @@
"node": ">=4"
}
},
+ "node_modules/assertion-error": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
+ "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/babel-plugin-jsx-dom-expressions": {
"version": "0.38.5",
"resolved": "https://registry.npmjs.org/babel-plugin-jsx-dom-expressions/-/babel-plugin-jsx-dom-expressions-0.38.5.tgz",
@@ -571,6 +729,16 @@
"dev": true,
"license": "MIT/X11"
},
+ "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/caniuse-lite": {
"version": "1.0.30001655",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001655.tgz",
@@ -592,6 +760,23 @@
],
"license": "CC-BY-4.0"
},
+ "node_modules/chai": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.1.tgz",
+ "integrity": "sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==",
+ "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": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
@@ -607,6 +792,16 @@
"node": ">=4"
}
},
+ "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/color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
@@ -624,6 +819,19 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
"node_modules/convert-source-map": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
@@ -631,12 +839,39 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/cssstyle": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.1.0.tgz",
+ "integrity": "sha512-h66W1URKpBS5YMI/V8PyXvTMFT8SupJ1IzoIV8IeBC/ji8WVmrO8dGlTi+2dh6whmdk6BiKJLD/ZBkhWbcg6nA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "rrweb-cssom": "^0.7.1"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/csstype": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
"license": "MIT"
},
+ "node_modules/data-urls": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz",
+ "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "whatwg-mimetype": "^4.0.0",
+ "whatwg-url": "^14.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/debug": {
"version": "4.3.6",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz",
@@ -655,6 +890,33 @@
}
}
},
+ "node_modules/decimal.js": {
+ "version": "10.4.3",
+ "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz",
+ "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "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/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
"node_modules/electron-to-chromium": {
"version": "1.5.13",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.13.tgz",
@@ -662,6 +924,19 @@
"dev": true,
"license": "ISC"
},
+ "node_modules/entities": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
+ "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
"node_modules/esbuild": {
"version": "0.21.5",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
@@ -721,6 +996,46 @@
"node": ">=0.8.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/form-data": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+ "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "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/gensync": {
"version": "1.0.0-beta.2",
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
@@ -731,6 +1046,16 @@
"node": ">=6.9.0"
}
},
+ "node_modules/get-func-name": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz",
+ "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ }
+ },
"node_modules/globals": {
"version": "11.12.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
@@ -751,6 +1076,19 @@
"node": ">=4"
}
},
+ "node_modules/html-encoding-sniffer": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz",
+ "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "whatwg-encoding": "^3.1.1"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/html-entities": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz",
@@ -758,6 +1096,47 @@
"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==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "^7.1.0",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/https-proxy-agent": {
+ "version": "7.0.5",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz",
+ "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "^7.0.2",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/iconv-lite": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+ "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/immutable": {
"version": "4.3.7",
"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz",
@@ -765,6 +1144,13 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/is-potential-custom-element-name": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
+ "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/is-what": {
"version": "4.1.16",
"resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz",
@@ -785,6 +1171,47 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/jsdom": {
+ "version": "25.0.0",
+ "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-25.0.0.tgz",
+ "integrity": "sha512-OhoFVT59T7aEq75TVw9xxEfkXgacpqAhQaYgP9y/fDqWQCMB/b1H66RfmPm/MaeaAIU9nDwMOVTlPN51+ao6CQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cssstyle": "^4.0.1",
+ "data-urls": "^5.0.0",
+ "decimal.js": "^10.4.3",
+ "form-data": "^4.0.0",
+ "html-encoding-sniffer": "^4.0.0",
+ "http-proxy-agent": "^7.0.2",
+ "https-proxy-agent": "^7.0.5",
+ "is-potential-custom-element-name": "^1.0.1",
+ "nwsapi": "^2.2.12",
+ "parse5": "^7.1.2",
+ "rrweb-cssom": "^0.7.1",
+ "saxes": "^6.0.0",
+ "symbol-tree": "^3.2.4",
+ "tough-cookie": "^4.1.4",
+ "w3c-xmlserializer": "^5.0.0",
+ "webidl-conversions": "^7.0.0",
+ "whatwg-encoding": "^3.1.1",
+ "whatwg-mimetype": "^4.0.0",
+ "whatwg-url": "^14.0.0",
+ "ws": "^8.18.0",
+ "xml-name-validator": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "canvas": "^2.11.2"
+ },
+ "peerDependenciesMeta": {
+ "canvas": {
+ "optional": true
+ }
+ }
+ },
"node_modules/jsesc": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
@@ -811,6 +1238,16 @@
"node": ">=6"
}
},
+ "node_modules/loupe": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.1.tgz",
+ "integrity": "sha512-edNu/8D5MKVfGVFRhFf8aAxiTM6Wumfz5XsaatSxlD3w4R1d/WEKUTydCdPGbl9K7QG/Ca3GnDV2sIKIpXRQcw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "get-func-name": "^2.0.1"
+ }
+ },
"node_modules/lru-cache": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
@@ -821,6 +1258,16 @@
"yallist": "^3.0.2"
}
},
+ "node_modules/magic-string": {
+ "version": "0.30.11",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz",
+ "integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0"
+ }
+ },
"node_modules/merge-anything": {
"version": "5.1.7",
"resolved": "https://registry.npmjs.org/merge-anything/-/merge-anything-5.1.7.tgz",
@@ -837,6 +1284,29 @@
"url": "https://github.com/sponsors/mesqueeb"
}
},
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
"node_modules/moment": {
"version": "2.30.1",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
@@ -879,6 +1349,43 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/nwsapi": {
+ "version": "2.2.12",
+ "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.12.tgz",
+ "integrity": "sha512-qXDmcVlZV4XRtKFzddidpfVP4oMSGhga+xdMc25mv8kaLUHtgzCDhUxkrN8exkGdTlLNaXj7CV3GtON7zuGZ+w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/parse5": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz",
+ "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "entities": "^4.4.0"
+ },
+ "funding": {
+ "url": "https://github.com/inikulin/parse5?sponsor=1"
+ }
+ },
+ "node_modules/pathe": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz",
+ "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==",
+ "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.0.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz",
@@ -915,6 +1422,37 @@
"node": "^10 || ^12 || >=14"
}
},
+ "node_modules/psl": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
+ "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "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,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/querystringify": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
+ "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/requires-port": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
+ "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/rollup": {
"version": "4.21.2",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.21.2.tgz",
@@ -951,6 +1489,13 @@
"fsevents": "~2.3.2"
}
},
+ "node_modules/rrweb-cssom": {
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.7.1.tgz",
+ "integrity": "sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/rxjs": {
"version": "7.8.1",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
@@ -961,6 +1506,13 @@
"tslib": "^2.1.0"
}
},
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/sass-embedded": {
"version": "1.78.0",
"resolved": "https://registry.npmjs.org/sass-embedded/-/sass-embedded-1.78.0.tgz",
@@ -1370,6 +1922,19 @@
"url": "https://github.com/chalk/supports-color?sponsor=1"
}
},
+ "node_modules/saxes": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz",
+ "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "xmlchars": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=v12.22.7"
+ }
+ },
"node_modules/semver": {
"version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
@@ -1401,6 +1966,13 @@
"seroval": "^1.0"
}
},
+ "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/solid-js": {
"version": "1.8.22",
"resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.8.22.tgz",
@@ -1437,6 +2009,20 @@
"node": ">=0.10.0"
}
},
+ "node_modules/stackback": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
+ "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/std-env": {
+ "version": "3.7.0",
+ "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.7.0.tgz",
+ "integrity": "sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
@@ -1450,6 +2036,57 @@
"node": ">=4"
}
},
+ "node_modules/symbol-tree": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
+ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/tinybench": {
+ "version": "2.9.0",
+ "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
+ "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/tinyexec": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.0.tgz",
+ "integrity": "sha512-tVGE0mVJPGb0chKhqmsoosjsS+qUnJVGJpZgsHYQcGoPlG3B51R3PouqTgEGH2Dc9jjFyOqOpix6ZHNMXp1FZg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/tinypool": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.1.tgz",
+ "integrity": "sha512-URZYihUbRPcGv95En+sz6MfghfIc2OJ1sv/RmhWZLouPY0/8Vo80viwPvg3dlaS9fuq7fQMEfgRRK7BBZThBEA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.0.0 || >=20.0.0"
+ }
+ },
+ "node_modules/tinyrainbow": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz",
+ "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==",
+ "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/to-fast-properties": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
@@ -1460,6 +2097,35 @@
"node": ">=4"
}
},
+ "node_modules/tough-cookie": {
+ "version": "4.1.4",
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz",
+ "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "psl": "^1.1.33",
+ "punycode": "^2.1.1",
+ "universalify": "^0.2.0",
+ "url-parse": "^1.5.3"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/tr46": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz",
+ "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "punycode": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/tslib": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz",
@@ -1481,6 +2147,25 @@
"node": ">=14.17"
}
},
+ "node_modules/undici-types": {
+ "version": "6.19.8",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
+ "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "peer": true
+ },
+ "node_modules/universalify": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
+ "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4.0.0"
+ }
+ },
"node_modules/update-browserslist-db": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz",
@@ -1512,6 +2197,17 @@
"browserslist": ">= 4.21.0"
}
},
+ "node_modules/url-parse": {
+ "version": "1.5.10",
+ "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
+ "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "querystringify": "^2.1.1",
+ "requires-port": "^1.0.0"
+ }
+ },
"node_modules/validate-html-nesting": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/validate-html-nesting/-/validate-html-nesting-1.2.2.tgz",
@@ -1586,6 +2282,28 @@
}
}
},
+ "node_modules/vite-node": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.1.tgz",
+ "integrity": "sha512-N/mGckI1suG/5wQI35XeR9rsMsPqKXzq1CdUndzVstBj/HvyxxGctwnK6WX43NGt5L3Z5tcRf83g4TITKJhPrA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cac": "^6.7.14",
+ "debug": "^4.3.6",
+ "pathe": "^1.1.2",
+ "vite": "^5.0.0"
+ },
+ "bin": {
+ "vite-node": "vite-node.mjs"
+ },
+ "engines": {
+ "node": "^18.0.0 || >=20.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
"node_modules/vite-plugin-solid": {
"version": "2.10.2",
"resolved": "https://registry.npmjs.org/vite-plugin-solid/-/vite-plugin-solid-2.10.2.tgz",
@@ -1626,6 +2344,187 @@
}
}
},
+ "node_modules/vitest": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.1.tgz",
+ "integrity": "sha512-97We7/VC0e9X5zBVkvt7SGQMGrRtn3KtySFQG5fpaMlS+l62eeXRQO633AYhSTC3z7IMebnPPNjGXVGNRFlxBA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/expect": "2.1.1",
+ "@vitest/mocker": "2.1.1",
+ "@vitest/pretty-format": "^2.1.1",
+ "@vitest/runner": "2.1.1",
+ "@vitest/snapshot": "2.1.1",
+ "@vitest/spy": "2.1.1",
+ "@vitest/utils": "2.1.1",
+ "chai": "^5.1.1",
+ "debug": "^4.3.6",
+ "magic-string": "^0.30.11",
+ "pathe": "^1.1.2",
+ "std-env": "^3.7.0",
+ "tinybench": "^2.9.0",
+ "tinyexec": "^0.3.0",
+ "tinypool": "^1.0.0",
+ "tinyrainbow": "^1.2.0",
+ "vite": "^5.0.0",
+ "vite-node": "2.1.1",
+ "why-is-node-running": "^2.3.0"
+ },
+ "bin": {
+ "vitest": "vitest.mjs"
+ },
+ "engines": {
+ "node": "^18.0.0 || >=20.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ },
+ "peerDependencies": {
+ "@edge-runtime/vm": "*",
+ "@types/node": "^18.0.0 || >=20.0.0",
+ "@vitest/browser": "2.1.1",
+ "@vitest/ui": "2.1.1",
+ "happy-dom": "*",
+ "jsdom": "*"
+ },
+ "peerDependenciesMeta": {
+ "@edge-runtime/vm": {
+ "optional": true
+ },
+ "@types/node": {
+ "optional": true
+ },
+ "@vitest/browser": {
+ "optional": true
+ },
+ "@vitest/ui": {
+ "optional": true
+ },
+ "happy-dom": {
+ "optional": true
+ },
+ "jsdom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/w3c-xmlserializer": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz",
+ "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "xml-name-validator": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/webidl-conversions": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
+ "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/whatwg-encoding": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz",
+ "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "iconv-lite": "0.6.3"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/whatwg-mimetype": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz",
+ "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/whatwg-url": {
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.0.0.tgz",
+ "integrity": "sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tr46": "^5.0.0",
+ "webidl-conversions": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "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/ws": {
+ "version": "8.18.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
+ "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": ">=5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/xml-name-validator": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz",
+ "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/xmlchars": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
+ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/yallist": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
diff --git a/gui/package.json b/gui/package.json
index 85bba22..b731d96 100644
--- a/gui/package.json
+++ b/gui/package.json
@@ -4,18 +4,22 @@
"version": "0.0.0",
"type": "module",
"scripts": {
+ "start": "vite",
"dev": "vite",
"build": "tsc -b && vite build",
- "preview": "vite preview"
+ "preview": "vite preview",
+ "test": "vitest"
},
"dependencies": {
"moment": "^2.30.1",
"solid-js": "^1.8.20"
},
"devDependencies": {
+ "jsdom": "^25.0.0",
"sass-embedded": "^1.78.0",
"typescript": "^5.5.3",
"vite": "^5.4.1",
- "vite-plugin-solid": "^2.10.2"
+ "vite-plugin-solid": "^2.10.2",
+ "vitest": "^2.1.1"
}
}
diff --git a/gui/src/ajax/backend.ts b/gui/src/ajax/backend.ts
index d58e513..90a7ea5 100644
--- a/gui/src/ajax/backend.ts
+++ b/gui/src/ajax/backend.ts
@@ -1,5 +1,58 @@
+import { BalanceMap, newCategoryBalanceMapFromDto } from "../types/balanceMap";
+import { BudgetMap, newBudgetMapFromDto } from "../types/budgetMap";
+import { Category, newCategoryFromDto } from "../types/category";
+
const backendUrl = "http://localhost:8000";
-async function getAllAccounts(): Promise<Account[]> {
- return;
+function makeGetter<T>(params: FetcherMakerParams<T>): Getter<T> {
+ const parser =
+ "parser" in params
+ ? params.parser
+ : (dto: unknown[]) => <T>dto.map(params.elementParser);
+
+ return async function () {
+ const response = await fetch(`${backendUrl}${params.path}`);
+ const dto = await response.json();
+
+ try {
+ return parser(dto);
+ } catch (err) {
+ console.error(`Error parsing DTO. ${err?.toString() ?? ""}`);
+ return null;
+ }
+ };
}
+
+export const getAllCategories: Getter<Category[]> = async () => {
+ const balances = await getAllCategoryBalances();
+ const budgets = await getAllCurrentCategoryBudgets();
+ return makeGetter<Category[]>({
+ path: "/category",
+ elementParser: newCategoryFromDto(balances, budgets),
+ })();
+};
+
+export const getAllCategoryBalances: Getter<BalanceMap<Category>> = makeGetter<
+ BalanceMap<Category>
+>({
+ path: "/balance/category",
+ parser: newCategoryBalanceMapFromDto,
+});
+
+export const getAllCurrentCategoryBudgets: Getter<BudgetMap<Category>> =
+ makeGetter<BudgetMap<Category>>({
+ path: "/budget/category?current",
+ parser: newBudgetMapFromDto,
+ });
+
+type FetcherMakerParams<T> = {
+ path: string;
+} & AnyParser<T>;
+
+type Parser<T> = { parser: (dto: unknown) => T | null };
+type ElementParser<T> = { elementParser: (dto: unknown) => T | null };
+type AnyParser<T> = T extends any[]
+ ? Parser<T> | ElementParser<T[number]>
+ : Parser<T>;
+
+type Getter<T> = () => Promise<T | null>;
diff --git a/gui/src/components/app/App.tsx b/gui/src/components/app/App.tsx
index cd9f9ee..e9ef742 100644
--- a/gui/src/components/app/App.tsx
+++ b/gui/src/components/app/App.tsx
@@ -1,53 +1,33 @@
-import Category from '../../types/category';
-import Gbp from '../../types/currency/gbp';
+import { createResource, Match, Resource, Switch } from 'solid-js';
import Histogram from '../histogram/Histogram'
import './App.css'
+import { getAllCategories } from '../../ajax/backend';
+import { Category } from '../../types/category';
function App() {
+ const [categories] = createResource(getAllCategories);
+
return (
- <>
- <Histogram categories={categories} />
- </>
- )
+ <Switch>
+ <Match when={categories.state === 'ready'}>
+ <Histogram categories={(categories as Extract<Resource<Category[]>, { state: 'ready' }>)()} />
+ </Match>
+ <Match when={categories.state === 'pending'}>
+ <p>Loading...</p>
+ </Match>
+ <Match when={categories.state === 'unresolved'}>
+ <p>Unresolved!</p>
+ </Match>
+ <Match when={categories.state === 'errored'}>
+ <p>Error!</p>
+ <p>{JSON.stringify(categories.error, null, 2)}</p>
+ <p>{JSON.stringify(categories(), null, 2)}</p>
+ </Match>
+ <Match when={categories.state === 'refreshing'}>
+ <p>Refreshing!</p>
+ </Match>
+ </Switch>
+ );
}
-const categories: Category[] = [
- {
- id: 0,
- name: 'Groceries',
- currentBalance: new Gbp(71, 37, 1),
- currentBudget: {
- quantity: new Gbp(65, 0, 1),
- period: 7,
- },
- },
- {
- id: 1,
- name: 'Stationery',
- currentBalance: new Gbp(10, 12, -1),
- currentBudget: {
- quantity: new Gbp(12, 0, 1),
- period: 30,
- },
- },
- {
- id: 2,
- name: 'Holidays',
- currentBalance: new Gbp(325, 0, 1),
- currentBudget: {
- quantity: new Gbp(1000, 0, 1),
- period: 365,
- },
- },
- {
- id: 3,
- name: 'Wages',
- currentBalance: new Gbp(1025, 60, -1),
- currentBudget: {
- quantity: new Gbp(2167, 7, -1),
- period: 30.25,
- },
- },
-].map(c => ({ ...c, balanceToBudgetRatio: c.currentBalance.valueOf() / c.currentBudget.quantity.valueOf() }));
-
export default App
diff --git a/gui/src/components/histogram/Histogram.tsx b/gui/src/components/histogram/Histogram.tsx
index ee4b7b0..3a8309b 100644
--- a/gui/src/components/histogram/Histogram.tsx
+++ b/gui/src/components/histogram/Histogram.tsx
@@ -1,10 +1,18 @@
-import Category from "../../types/category";
+import { BalanceToBudgetRatio } from "../../types/balanceToBudgetRatio";
+import { Category } from "../../types/category";
import './Histogram.scss';
function Histogram({ categories }: Props) {
- const maxAbsBalanceToBudgetRatio = Math.max(
- ...categories.map((c) => Math.abs(c.balanceToBudgetRatio))
- );
+ if (categories === null) {
+ return (
+ <p>No categories to show</p>
+ );
+ }
+
+ const balanceToBudgetRatioNormaliser = BalanceToBudgetRatio.makeNormaliser(...categories.map(c => c.balanceToBudgetRatio));
+ for (const category of categories) {
+ category.balanceToBudgetRatio = balanceToBudgetRatioNormaliser(category.balanceToBudgetRatio);
+ }
return (
<table>
@@ -19,27 +27,24 @@ function Histogram({ categories }: Props) {
<tbody>
{
categories
- .sort((c1, c2) => c1.balanceToBudgetRatio - c2.balanceToBudgetRatio)
- .map((category) => Row({ category, maxAbsBalanceToBudgetRatio }))
+ .sort((c1, c2) => BalanceToBudgetRatio.compare(c1.balanceToBudgetRatio, c2.balanceToBudgetRatio))
+ .map((category) => Row({ category }))
}
</tbody>
</table>
);
}
-function Row({ category, maxAbsBalanceToBudgetRatio }: RowProps) {
- const normalisedRatio = category.balanceToBudgetRatio / maxAbsBalanceToBudgetRatio;
- const variant = getRowVariant(new Number(normalisedRatio).valueOf());
-
+function Row({ category }: RowProps) {
return (
<tr>
<td class="name">{category.name}</td>
<td
- class={`ratio ratio--${variant}`}
- style={`--normal-abs-ratio: ${Math.abs(normalisedRatio)};`}
+ class={`ratio ratio--${category.balanceToBudgetRatio?.recommendedAction ?? ''}`}
+ style={`--normal-abs-ratio: ${Math.abs(category.balanceToBudgetRatio?.normalisedRatio ?? 0)};`}
>
<span class="ratio__text">
- {category.balanceToBudgetRatio.toFixed(2)}
+ {category.balanceToBudgetRatio?.toString() ?? ''}
</span>
<span class="ratio__bar"></span>
</td>
@@ -48,23 +53,12 @@ function Row({ category, maxAbsBalanceToBudgetRatio }: RowProps) {
);
}
-function getRowVariant(balanceToBudgetRatio: number) {
- if (balanceToBudgetRatio < 0) {
- return 'arrears';
- }
- if (balanceToBudgetRatio >= 1) {
- return 'spend';
- }
- return 'save';
-}
-
type Props = {
- categories: Category[],
+ categories: Category[] | null,
};
type RowProps = {
category: Category,
- maxAbsBalanceToBudgetRatio: number,
};
export default Histogram;
diff --git a/gui/src/types/BalanceToBudgetRatio.spec.ts b/gui/src/types/BalanceToBudgetRatio.spec.ts
new file mode 100644
index 0000000..3a956ea
--- /dev/null
+++ b/gui/src/types/BalanceToBudgetRatio.spec.ts
@@ -0,0 +1,307 @@
+import { describe, expect, it } from "vitest";
+import { BalanceToBudgetRatio } from "./balanceToBudgetRatio";
+import { Gbp } from "./currency/gbp";
+
+describe("balanceToBudgetRatio", () => {
+ it("new() returns null when budget is null", () => {
+ const result = BalanceToBudgetRatio.new(new Gbp(1, 0, 1), null);
+ expect(result).toBeNull();
+ });
+
+ it("toString() rounds to 2 decimal places", () => {
+ const mockCurrencyPi = { valueOf: () => Math.PI };
+ const mockCurrencyUnit = { valueOf: () => 1 };
+ const sut = BalanceToBudgetRatio.new(mockCurrencyPi, {
+ quantity: mockCurrencyUnit,
+ period: 42,
+ });
+
+ const result = sut?.toString();
+
+ expect(result).toBe("3.14");
+ });
+
+ it("new() sets normalisedRatio to 1 when balance and budget are equal and positive", () => {
+ const twoFifty = new Gbp(2, 50, 1);
+ const result = BalanceToBudgetRatio.new(twoFifty, {
+ quantity: twoFifty,
+ period: 42,
+ });
+ expect(result?.normalisedRatio).toBeCloseTo(1.0);
+ });
+
+ it("new() sets normalisedRatio to 2 when balance is twice budget", () => {
+ const twoFifty = new Gbp(2, 50, 1);
+ const oneTwentyFive = new Gbp(1, 25, 1);
+ const result = BalanceToBudgetRatio.new(twoFifty, {
+ quantity: oneTwentyFive,
+ period: 42,
+ });
+ expect(result?.normalisedRatio).toBeCloseTo(2.0);
+ });
+
+ it("new() sets normalisedRatio to -2 when a negative balance is -1 * twice budget", () => {
+ const minusTwoFifty = new Gbp(2, 50, -1);
+ const oneTwentyFive = new Gbp(1, 25, 1);
+ const result = BalanceToBudgetRatio.new(minusTwoFifty, {
+ quantity: oneTwentyFive,
+ period: 42,
+ });
+ expect(result?.normalisedRatio).toBeCloseTo(-2.0);
+ });
+
+ it("new() sets normalisedRatio to -0.5 when balance is -1 * half a negative budget", () => {
+ const oneTwentyFive = new Gbp(1, 25, 1);
+ const minusTwoFifty = new Gbp(2, 50, -1);
+ const result = BalanceToBudgetRatio.new(oneTwentyFive, {
+ quantity: minusTwoFifty,
+ period: 42,
+ });
+ expect(result?.normalisedRatio).toBeCloseTo(-0.5);
+ });
+
+ it("new() sets normalisedRatio to 1 when balance and budget are equal and negative", () => {
+ const minusTwoFifty = new Gbp(2, 50, -1);
+ const result = BalanceToBudgetRatio.new(minusTwoFifty, {
+ quantity: minusTwoFifty,
+ period: 42,
+ });
+ expect(result?.normalisedRatio).toBeCloseTo(1.0);
+ });
+
+ it('new() sets recommended action to "arrears" when ratio is less than 0', () => {
+ const oneTwentyFive = new Gbp(1, 25, 1);
+ const minusTwoFifty = new Gbp(2, 50, -1);
+
+ const result = BalanceToBudgetRatio.new(oneTwentyFive, {
+ quantity: minusTwoFifty,
+ period: 42,
+ });
+
+ expect(result?.recommendedAction).toBe("arrears");
+ });
+
+ it('new() sets recommended action to "save" when ratio is between 0 and 1', () => {
+ const oneTwentyFive = new Gbp(1, 25, 1);
+ const twoFifty = new Gbp(2, 50, 1);
+
+ const result = BalanceToBudgetRatio.new(oneTwentyFive, {
+ quantity: twoFifty,
+ period: 42,
+ });
+
+ expect(result?.recommendedAction).toBe("save");
+ });
+
+ it('new() sets recommended action to "spend" when ratio is larger than 1', () => {
+ const oneTwentyFive = new Gbp(1, 25, 1);
+ const twoFifty = new Gbp(2, 50, 1);
+
+ const result = BalanceToBudgetRatio.new(twoFifty, {
+ quantity: oneTwentyFive,
+ period: 42,
+ });
+
+ expect(result?.recommendedAction).toBe("spend");
+ });
+
+ it("normalises to a factor of 2 when all ratios are positive", () => {
+ const oneTwentyFive = new Gbp(1, 25, 1);
+ const twoFifty = new Gbp(2, 50, 1);
+ const twoOverOne = BalanceToBudgetRatio.new(twoFifty, {
+ quantity: oneTwentyFive,
+ period: 42,
+ });
+ const twoOverTwo = BalanceToBudgetRatio.new(twoFifty, {
+ quantity: twoFifty,
+ period: 42,
+ });
+ const oneOverOne = BalanceToBudgetRatio.new(oneTwentyFive, {
+ quantity: oneTwentyFive,
+ period: 42,
+ });
+ const oneOverTwo = BalanceToBudgetRatio.new(oneTwentyFive, {
+ quantity: twoFifty,
+ period: 42,
+ });
+
+ const normaliser = BalanceToBudgetRatio.makeNormaliser(
+ twoOverOne,
+ twoOverTwo,
+ oneOverOne,
+ oneOverTwo,
+ );
+
+ expect(normaliser(twoOverOne).normalisedRatio).toBeCloseTo(1.0);
+ expect(normaliser(twoOverTwo).normalisedRatio).toBeCloseTo(0.5);
+ expect(normaliser(oneOverOne).normalisedRatio).toBeCloseTo(0.5);
+ expect(normaliser(oneOverTwo).normalisedRatio).toBeCloseTo(0.25);
+ });
+
+ it("normalises to a factor of 2 when all ratios are negative", () => {
+ const oneTwentyFive = new Gbp(1, 25, 1);
+ const twoFifty = new Gbp(2, 50, 1);
+ const minusOneTwentyFive = new Gbp(1, 25, -1);
+ const minusTwoFifty = new Gbp(2, 50, -1);
+ const minusTwoOverOne = BalanceToBudgetRatio.new(minusTwoFifty, {
+ quantity: oneTwentyFive,
+ period: 42,
+ });
+ const minusTwoOverTwo = BalanceToBudgetRatio.new(minusTwoFifty, {
+ quantity: twoFifty,
+ period: 42,
+ });
+ const minusOneOverOne = BalanceToBudgetRatio.new(minusOneTwentyFive, {
+ quantity: oneTwentyFive,
+ period: 42,
+ });
+ const minusOneOverTwo = BalanceToBudgetRatio.new(minusOneTwentyFive, {
+ quantity: twoFifty,
+ period: 42,
+ });
+
+ const normaliser = BalanceToBudgetRatio.makeNormaliser(
+ minusTwoOverOne,
+ minusTwoOverTwo,
+ minusOneOverOne,
+ minusOneOverTwo,
+ );
+
+ expect(normaliser(minusTwoOverOne).normalisedRatio).toBeCloseTo(-1.0);
+ expect(normaliser(minusTwoOverTwo).normalisedRatio).toBeCloseTo(-0.5);
+ expect(normaliser(minusOneOverOne).normalisedRatio).toBeCloseTo(-0.5);
+ expect(normaliser(minusOneOverTwo).normalisedRatio).toBeCloseTo(-0.25);
+ });
+
+ it("normalises to a factor of 2 when ratios are a mix of positive and negative", () => {
+ const oneTwentyFive = new Gbp(1, 25, 1);
+ const twoFifty = new Gbp(2, 50, 1);
+ const minusOneTwentyFive = new Gbp(1, 25, -1);
+ const minusTwoFifty = new Gbp(2, 50, -1);
+ const twoOverOne = BalanceToBudgetRatio.new(twoFifty, {
+ quantity: oneTwentyFive,
+ period: 42,
+ });
+ const twoOverTwo = BalanceToBudgetRatio.new(twoFifty, {
+ quantity: twoFifty,
+ period: 42,
+ });
+ const oneOverOne = BalanceToBudgetRatio.new(oneTwentyFive, {
+ quantity: oneTwentyFive,
+ period: 42,
+ });
+ const oneOverTwo = BalanceToBudgetRatio.new(oneTwentyFive, {
+ quantity: twoFifty,
+ period: 42,
+ });
+ const minusTwoOverOne = BalanceToBudgetRatio.new(minusTwoFifty, {
+ quantity: oneTwentyFive,
+ period: 42,
+ });
+ const minusTwoOverTwo = BalanceToBudgetRatio.new(minusTwoFifty, {
+ quantity: twoFifty,
+ period: 42,
+ });
+ const minusOneOverOne = BalanceToBudgetRatio.new(minusOneTwentyFive, {
+ quantity: oneTwentyFive,
+ period: 42,
+ });
+ const minusOneOverTwo = BalanceToBudgetRatio.new(minusOneTwentyFive, {
+ quantity: twoFifty,
+ period: 42,
+ });
+
+ const normaliser = BalanceToBudgetRatio.makeNormaliser(
+ twoOverOne,
+ twoOverTwo,
+ oneOverOne,
+ oneOverTwo,
+ minusTwoOverOne,
+ minusTwoOverTwo,
+ minusOneOverOne,
+ minusOneOverTwo,
+ );
+
+ expect(normaliser(twoOverOne).normalisedRatio).toBeCloseTo(1.0);
+ expect(normaliser(twoOverTwo).normalisedRatio).toBeCloseTo(0.5);
+ expect(normaliser(oneOverOne).normalisedRatio).toBeCloseTo(0.5);
+ expect(normaliser(oneOverTwo).normalisedRatio).toBeCloseTo(0.25);
+ expect(normaliser(minusTwoOverOne).normalisedRatio).toBeCloseTo(-1.0);
+ expect(normaliser(minusTwoOverTwo).normalisedRatio).toBeCloseTo(-0.5);
+ expect(normaliser(minusOneOverOne).normalisedRatio).toBeCloseTo(-0.5);
+ expect(normaliser(minusOneOverTwo).normalisedRatio).toBeCloseTo(-0.25);
+ });
+
+ it("sorts large positive ratios before small positive ratios", () => {
+ const oneTwentyFive = new Gbp(1, 25, 1);
+ const twoFifty = new Gbp(2, 50, 1);
+ const twoOverTwo = BalanceToBudgetRatio.new(twoFifty, {
+ quantity: twoFifty,
+ period: 42,
+ });
+ const oneOverTwo = BalanceToBudgetRatio.new(oneTwentyFive, {
+ quantity: twoFifty,
+ period: 42,
+ });
+
+ const resultLeftways = BalanceToBudgetRatio.compare(twoOverTwo, oneOverTwo);
+ const resultRightways = BalanceToBudgetRatio.compare(
+ oneOverTwo,
+ twoOverTwo,
+ );
+
+ expect(resultLeftways).toBeCloseTo(-resultRightways);
+ expect(resultLeftways).toBeLessThan(0);
+ });
+
+ it("sorts small negative ratios before large negative ratios", () => {
+ const twoFifty = new Gbp(2, 50, 1);
+ const minusOneTwentyFive = new Gbp(1, 25, -1);
+ const minusTwoFifty = new Gbp(2, 50, -1);
+ const minusTwoOverTwo = BalanceToBudgetRatio.new(minusTwoFifty, {
+ quantity: twoFifty,
+ period: 42,
+ });
+ const minusOneOverTwo = BalanceToBudgetRatio.new(minusOneTwentyFive, {
+ quantity: twoFifty,
+ period: 42,
+ });
+
+ const resultLeftways = BalanceToBudgetRatio.compare(
+ minusOneOverTwo,
+ minusTwoOverTwo,
+ );
+ const resultRightways = BalanceToBudgetRatio.compare(
+ minusTwoOverTwo,
+ minusOneOverTwo,
+ );
+
+ expect(resultLeftways).toBeCloseTo(-resultRightways);
+ expect(resultLeftways).toBeLessThan(0);
+ });
+
+ it("sorts positive ratios before negative ratios", () => {
+ const twoFifty = new Gbp(2, 50, 1);
+ const minusTwoFifty = new Gbp(2, 50, -1);
+ const twoOverTwo = BalanceToBudgetRatio.new(twoFifty, {
+ quantity: twoFifty,
+ period: 42,
+ });
+ const minusTwoOverTwo = BalanceToBudgetRatio.new(minusTwoFifty, {
+ quantity: twoFifty,
+ period: 42,
+ });
+
+ const resultLeftways = BalanceToBudgetRatio.compare(
+ twoOverTwo,
+ minusTwoOverTwo,
+ );
+ const resultRightways = BalanceToBudgetRatio.compare(
+ minusTwoOverTwo,
+ twoOverTwo,
+ );
+
+ expect(resultLeftways).toBeCloseTo(-resultRightways);
+ expect(resultLeftways).toBeLessThan(0);
+ });
+});
diff --git a/gui/src/types/account.ts b/gui/src/types/account.ts
index 4a3626c..a463304 100644
--- a/gui/src/types/account.ts
+++ b/gui/src/types/account.ts
@@ -1,5 +1,27 @@
-import { Moment } from "moment";
+import moment, { Moment } from "moment";
import Currency from "./currency/currency";
+import Gbp from "./currency/gbp";
+
+function newAccountFromDto(dto: unknown): Account {
+ const dtoDangerous = dto as AccountDto;
+ return {
+ id: dtoDangerous.id,
+ name: dtoDangerous.name,
+ openingBalance: new Gbp(
+ Math.floor(Math.abs(dtoDangerous.opening_balance) / 100),
+ Math.abs(dtoDangerous.opening_balance) % 100,
+ dtoDangerous.opening_balance < 0 ? -1 : 1,
+ ),
+ openingDate: moment(dtoDangerous.opening_date),
+ };
+}
+
+interface AccountDto {
+ readonly id: number;
+ readonly name: string;
+ readonly opening_balance: number;
+ readonly opening_date: string;
+}
interface Account {
readonly id: number;
@@ -8,4 +30,4 @@ interface Account {
readonly openingDate: Moment;
}
-export default Account;
+export { type Account, newAccountFromDto };
diff --git a/gui/src/types/balanceMap.ts b/gui/src/types/balanceMap.ts
new file mode 100644
index 0000000..760843f
--- /dev/null
+++ b/gui/src/types/balanceMap.ts
@@ -0,0 +1,17 @@
+import { Category } from "./category";
+import Currency from "./currency/currency";
+import { newGbpFromDto } from "./currency/gbp";
+
+type BalanceMap<T extends { id: any }> = {
+ [id in T["id"]]: Currency;
+};
+
+function newCategoryBalanceMapFromDto(dto: unknown): BalanceMap<Category> {
+ return Object.fromEntries(
+ (dto as { category_id: Category["id"]; balance: number }[]).map(
+ ({ category_id, balance }) => [category_id, newGbpFromDto(balance)],
+ ),
+ );
+}
+
+export { type BalanceMap, newCategoryBalanceMapFromDto };
diff --git a/gui/src/types/balanceToBudgetRatio.ts b/gui/src/types/balanceToBudgetRatio.ts
new file mode 100644
index 0000000..2f8000c
--- /dev/null
+++ b/gui/src/types/balanceToBudgetRatio.ts
@@ -0,0 +1,83 @@
+import { Budget } from "./budget";
+import Currency from "./currency/currency";
+
+class BalanceToBudgetRatio {
+ toString(): string {
+ return this._ratio?.toFixed(2) ?? "";
+ }
+
+ public readonly recommendedAction: "arrears" | "save" | "spend" | null;
+ public readonly normalisedRatio: number | null;
+
+ private constructor(
+ private readonly _ratio: number | null,
+ private readonly _normalisationFactor: number,
+ ) {
+ if (this._ratio === null) {
+ this.normalisedRatio = null;
+ this.recommendedAction = null;
+ return;
+ }
+
+ this.normalisedRatio = this._ratio * this._normalisationFactor;
+
+ if (this._ratio < 0) {
+ this.recommendedAction = "arrears";
+ } else if (this._ratio >= 1) {
+ this.recommendedAction = "spend";
+ } else {
+ this.recommendedAction = "save";
+ }
+ }
+
+ static new(balance: Currency, budget: Budget | null) {
+ if (budget === null || budget.quantity === 0 || budget.quantity === -0) {
+ return null;
+ }
+
+ return new BalanceToBudgetRatio(
+ balance.valueOf() / budget.quantity.valueOf(),
+ 1,
+ );
+ }
+
+ static makeNormaliser(
+ ...balanceToBudgetRatios: (BalanceToBudgetRatio | null)[]
+ ): (r: BalanceToBudgetRatio | null) => BalanceToBudgetRatio {
+ const validRatios = balanceToBudgetRatios.filter(
+ (r) =>
+ r &&
+ r._ratio &&
+ !isNaN(r._ratio) &&
+ Infinity > r._ratio &&
+ -Infinity < r._ratio,
+ );
+
+ const maxAbsRatio = Math.max(
+ ...validRatios.map((r) => Math.abs(r?._ratio ?? 0)),
+ );
+
+ const normalisationFactor = 1 / maxAbsRatio;
+ return (r) =>
+ new BalanceToBudgetRatio(r?._ratio ?? null, normalisationFactor);
+ }
+
+ static compare(
+ lhs: BalanceToBudgetRatio | null,
+ rhs: BalanceToBudgetRatio | null,
+ ): number {
+ if (lhs === null || lhs._ratio === null) {
+ if (rhs === null || rhs._ratio === null) {
+ return 0;
+ }
+ return 1;
+ }
+ if (rhs === null || rhs._ratio === null) {
+ return -1;
+ }
+
+ return rhs._ratio - lhs._ratio;
+ }
+}
+
+export { BalanceToBudgetRatio };
diff --git a/gui/src/types/budget.ts b/gui/src/types/budget.ts
index 86bae2c..5a19a1b 100644
--- a/gui/src/types/budget.ts
+++ b/gui/src/types/budget.ts
@@ -1,8 +1,12 @@
import Currency from "./currency/currency";
interface Budget {
- quantity: Currency,
- period: number,
-};
+ quantity: Currency;
+ period: number;
+}
-export default Budget;
+function newBudgetFromDto(dto: unknown) {
+ return dto as Budget;
+}
+
+export { type Budget, newBudgetFromDto };
diff --git a/gui/src/types/budgetMap.ts b/gui/src/types/budgetMap.ts
new file mode 100644
index 0000000..5fa09f2
--- /dev/null
+++ b/gui/src/types/budgetMap.ts
@@ -0,0 +1,16 @@
+import { Budget, newBudgetFromDto } from "./budget";
+import { Category } from "./category";
+
+type BudgetMap<T extends { id: any }> = {
+ [id in T["id"]]: Budget | null;
+};
+
+function newCategoryBudgetMapFromDto(dto: unknown): BudgetMap<Category> {
+ return Object.fromEntries(
+ (dto as { category_id: number; budget: unknown }[]).map(
+ ({ category_id, budget }) => [category_id, newBudgetFromDto(budget)],
+ ),
+ );
+}
+
+export { type BudgetMap, newCategoryBudgetMapFromDto as newBudgetMapFromDto };
diff --git a/gui/src/types/category.ts b/gui/src/types/category.ts
index af4526b..005c041 100644
--- a/gui/src/types/category.ts
+++ b/gui/src/types/category.ts
@@ -1,12 +1,45 @@
-import Budget from "./budget";
+import { BalanceMap } from "./balanceMap";
+import { BalanceToBudgetRatio } from "./balanceToBudgetRatio";
+import { Budget } from "./budget";
+import { BudgetMap } from "./budgetMap";
import Currency from "./currency/currency";
interface Category {
- id: number,
- name: string,
- currentBalance: Currency,
- currentBudget: Budget,
- balanceToBudgetRatio: number,
-};
-
-export default Category;
+ id: number;
+ name: string;
+ currentBalance: Currency;
+ currentBudget: Budget | null;
+ balanceToBudgetRatio: BalanceToBudgetRatio | null;
+}
+
+interface CategoryDto {
+ id: number;
+ name: string;
+}
+
+function newCategoryFromDto(
+ balances: BalanceMap<Category> | null,
+ budgets: BudgetMap<Category> | null,
+): (dto: unknown) => Category | null {
+ if (balances === null || budgets === null) {
+ return () => null;
+ }
+
+ return function (dto: unknown): Category {
+ const dtoDangerous = dto as CategoryDto;
+ const currentBalance = balances[dtoDangerous.id];
+ const currentBudget = budgets[dtoDangerous.id];
+ return {
+ id: dtoDangerous.id,
+ name: dtoDangerous.name,
+ currentBalance,
+ currentBudget,
+ balanceToBudgetRatio: BalanceToBudgetRatio.new(
+ currentBalance,
+ currentBudget,
+ ),
+ };
+ };
+}
+
+export { type Category, newCategoryFromDto };
diff --git a/gui/src/types/currency/gbp.ts b/gui/src/types/currency/gbp.ts
index ce24e48..b88aae3 100644
--- a/gui/src/types/currency/gbp.ts
+++ b/gui/src/types/currency/gbp.ts
@@ -43,4 +43,12 @@ class Gbp implements Currency {
}
}
-export default Gbp;
+function newGbpFromDto(dto: unknown): Gbp {
+ const signedPence = dto as number;
+ const pounds = Math.floor(Math.abs(signedPence) / 100);
+ const pence = Math.abs(signedPence) % 100;
+ const sign = signedPence < 0 ? -1 : 1;
+ return new Gbp(pounds, pence, sign);
+}
+
+export { Gbp, newGbpFromDto };
diff --git a/gui/src/types/dtos/accountDto.ts b/gui/src/types/dtos/accountDto.ts
deleted file mode 100644
index 0ed7622..0000000
--- a/gui/src/types/dtos/accountDto.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-interface AccountDto {
- readonly id: number;
- readonly name: string;
- readonly openingBalance: number;
- readonly openingDate: string;
-}
-
-export default AccountDto;
diff --git a/gui/yarn.lock b/gui/yarn.lock
deleted file mode 100644
index 937c75a..0000000
--- a/gui/yarn.lock
+++ /dev/null
@@ -1,690 +0,0 @@
-# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
-# yarn lockfile v1
-
-
-"@ampproject/remapping@^2.2.0":
- version "2.3.0"
- resolved "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz"
- integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==
- dependencies:
- "@jridgewell/gen-mapping" "^0.3.5"
- "@jridgewell/trace-mapping" "^0.3.24"
-
-"@babel/code-frame@^7.24.7":
- version "7.24.7"
- resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz"
- integrity sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==
- dependencies:
- "@babel/highlight" "^7.24.7"
- picocolors "^1.0.0"
-
-"@babel/compat-data@^7.25.2":
- version "7.25.4"
- resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.4.tgz"
- integrity sha512-+LGRog6RAsCJrrrg/IO6LGmpphNe5DiK30dGjCoxxeGv49B10/3XYGxPsAwrDlMFcFEvdAUavDT8r9k/hSyQqQ==
-
-"@babel/core@^7.0.0", "@babel/core@^7.0.0-0", "@babel/core@^7.20.12", "@babel/core@^7.23.3":
- version "7.25.2"
- resolved "https://registry.npmjs.org/@babel/core/-/core-7.25.2.tgz"
- integrity sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==
- dependencies:
- "@ampproject/remapping" "^2.2.0"
- "@babel/code-frame" "^7.24.7"
- "@babel/generator" "^7.25.0"
- "@babel/helper-compilation-targets" "^7.25.2"
- "@babel/helper-module-transforms" "^7.25.2"
- "@babel/helpers" "^7.25.0"
- "@babel/parser" "^7.25.0"
- "@babel/template" "^7.25.0"
- "@babel/traverse" "^7.25.2"
- "@babel/types" "^7.25.2"
- convert-source-map "^2.0.0"
- debug "^4.1.0"
- gensync "^1.0.0-beta.2"
- json5 "^2.2.3"
- semver "^6.3.1"
-
-"@babel/generator@^7.23.6", "@babel/generator@^7.25.0", "@babel/generator@^7.25.6":
- version "7.25.6"
- resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.25.6.tgz"
- integrity sha512-VPC82gr1seXOpkjAAKoLhP50vx4vGNlF4msF64dSFq1P8RfB+QAuJWGHPXXPc8QyfVWwwB/TNNU4+ayZmHNbZw==
- dependencies:
- "@babel/types" "^7.25.6"
- "@jridgewell/gen-mapping" "^0.3.5"
- "@jridgewell/trace-mapping" "^0.3.25"
- jsesc "^2.5.1"
-
-"@babel/helper-compilation-targets@^7.25.2":
- version "7.25.2"
- resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.2.tgz"
- integrity sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==
- dependencies:
- "@babel/compat-data" "^7.25.2"
- "@babel/helper-validator-option" "^7.24.8"
- browserslist "^4.23.1"
- lru-cache "^5.1.1"
- semver "^6.3.1"
-
-"@babel/helper-module-imports@^7.22.15", "@babel/helper-module-imports@^7.24.7":
- version "7.24.7"
- resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz"
- integrity sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==
- dependencies:
- "@babel/traverse" "^7.24.7"
- "@babel/types" "^7.24.7"
-
-"@babel/helper-module-imports@7.18.6":
- version "7.18.6"
- resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz"
- integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==
- dependencies:
- "@babel/types" "^7.18.6"
-
-"@babel/helper-module-transforms@^7.25.2":
- version "7.25.2"
- resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.2.tgz"
- integrity sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ==
- dependencies:
- "@babel/helper-module-imports" "^7.24.7"
- "@babel/helper-simple-access" "^7.24.7"
- "@babel/helper-validator-identifier" "^7.24.7"
- "@babel/traverse" "^7.25.2"
-
-"@babel/helper-plugin-utils@^7.24.7":
- version "7.24.8"
- resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.8.tgz"
- integrity sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==
-
-"@babel/helper-simple-access@^7.24.7":
- version "7.24.7"
- resolved "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz"
- integrity sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==
- dependencies:
- "@babel/traverse" "^7.24.7"
- "@babel/types" "^7.24.7"
-
-"@babel/helper-string-parser@^7.24.8":
- version "7.24.8"
- resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz"
- integrity sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==
-
-"@babel/helper-validator-identifier@^7.24.7":
- version "7.24.7"
- resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz"
- integrity sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==
-
-"@babel/helper-validator-option@^7.24.8":
- version "7.24.8"
- resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz"
- integrity sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==
-
-"@babel/helpers@^7.25.0":
- version "7.25.6"
- resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.6.tgz"
- integrity sha512-Xg0tn4HcfTijTwfDwYlvVCl43V6h4KyVVX2aEm4qdO/PC6L2YvzLHFdmxhoeSA3eslcE6+ZVXHgWwopXYLNq4Q==
- dependencies:
- "@babel/template" "^7.25.0"
- "@babel/types" "^7.25.6"
-
-"@babel/highlight@^7.24.7":
- version "7.24.7"
- resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz"
- integrity sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==
- dependencies:
- "@babel/helper-validator-identifier" "^7.24.7"
- chalk "^2.4.2"
- js-tokens "^4.0.0"
- picocolors "^1.0.0"
-
-"@babel/parser@^7.1.0", "@babel/parser@^7.20.7", "@babel/parser@^7.25.0", "@babel/parser@^7.25.6":
- version "7.25.6"
- resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.25.6.tgz"
- integrity sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==
- dependencies:
- "@babel/types" "^7.25.6"
-
-"@babel/plugin-syntax-jsx@^7.18.6":
- version "7.24.7"
- resolved "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.7.tgz"
- integrity sha512-6ddciUPe/mpMnOKv/U+RSd2vvVy+Yw/JfBB0ZHYjEZt9NLHmCUylNYlsbqCCS1Bffjlb0fCwC9Vqz+sBz6PsiQ==
- dependencies:
- "@babel/helper-plugin-utils" "^7.24.7"
-
-"@babel/template@^7.25.0":
- version "7.25.0"
- resolved "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz"
- integrity sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==
- dependencies:
- "@babel/code-frame" "^7.24.7"
- "@babel/parser" "^7.25.0"
- "@babel/types" "^7.25.0"
-
-"@babel/traverse@^7.24.7", "@babel/traverse@^7.25.2":
- version "7.25.6"
- resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.6.tgz"
- integrity sha512-9Vrcx5ZW6UwK5tvqsj0nGpp/XzqthkT0dqIc9g1AdtygFToNtTF67XzYS//dm+SAK9cp3B9R4ZO/46p63SCjlQ==
- dependencies:
- "@babel/code-frame" "^7.24.7"
- "@babel/generator" "^7.25.6"
- "@babel/parser" "^7.25.6"
- "@babel/template" "^7.25.0"
- "@babel/types" "^7.25.6"
- debug "^4.3.1"
- globals "^11.1.0"
-
-"@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.20.7", "@babel/types@^7.23.6", "@babel/types@^7.24.7", "@babel/types@^7.25.0", "@babel/types@^7.25.2", "@babel/types@^7.25.6":
- version "7.25.6"
- resolved "https://registry.npmjs.org/@babel/types/-/types-7.25.6.tgz"
- integrity sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==
- dependencies:
- "@babel/helper-string-parser" "^7.24.8"
- "@babel/helper-validator-identifier" "^7.24.7"
- to-fast-properties "^2.0.0"
-
-"@bufbuild/protobuf@^1.0.0":
- version "1.10.0"
- resolved "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-1.10.0.tgz"
- integrity sha512-QDdVFLoN93Zjg36NoQPZfsVH9tZew7wKDKyV5qRdj8ntT4wQCOradQjRaTdwMhWUYsgKsvCINKKm87FdEk96Ag==
-
-"@esbuild/linux-x64@0.21.5":
- version "0.21.5"
- resolved "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz"
- integrity sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==
-
-"@jridgewell/gen-mapping@^0.3.5":
- version "0.3.5"
- resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz"
- integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==
- dependencies:
- "@jridgewell/set-array" "^1.2.1"
- "@jridgewell/sourcemap-codec" "^1.4.10"
- "@jridgewell/trace-mapping" "^0.3.24"
-
-"@jridgewell/resolve-uri@^3.1.0":
- version "3.1.2"
- resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz"
- integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==
-
-"@jridgewell/set-array@^1.2.1":
- version "1.2.1"
- resolved "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz"
- integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==
-
-"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14":
- version "1.5.0"
- resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz"
- integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==
-
-"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25":
- version "0.3.25"
- resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz"
- integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==
- dependencies:
- "@jridgewell/resolve-uri" "^3.1.0"
- "@jridgewell/sourcemap-codec" "^1.4.14"
-
-"@rollup/rollup-linux-x64-gnu@4.21.2":
- version "4.21.2"
- resolved "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.21.2.tgz"
- integrity sha512-B90tYAUoLhU22olrafY3JQCFLnT3NglazdwkHyxNDYF/zAxJt5fJUB/yBoWFoIQ7SQj+KLe3iL4BhOMa9fzgpw==
-
-"@rollup/rollup-linux-x64-musl@4.21.2":
- version "4.21.2"
- resolved "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.21.2.tgz"
- integrity sha512-7twFizNXudESmC9oneLGIUmoHiiLppz/Xs5uJQ4ShvE6234K0VB1/aJYU3f/4g7PhssLGKBVCC37uRkkOi8wjg==
-
-"@types/babel__core@^7.20.4":
- version "7.20.5"
- resolved "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz"
- integrity sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==
- dependencies:
- "@babel/parser" "^7.20.7"
- "@babel/types" "^7.20.7"
- "@types/babel__generator" "*"
- "@types/babel__template" "*"
- "@types/babel__traverse" "*"
-
-"@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==
- dependencies:
- "@babel/types" "^7.0.0"
-
-"@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==
- dependencies:
- "@babel/parser" "^7.1.0"
- "@babel/types" "^7.0.0"
-
-"@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==
- dependencies:
- "@babel/types" "^7.20.7"
-
-"@types/estree@1.0.5":
- version "1.0.5"
- resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz"
- integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==
-
-ansi-styles@^3.2.1:
- 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==
- dependencies:
- color-convert "^1.9.0"
-
-babel-plugin-jsx-dom-expressions@^0.38.5:
- version "0.38.5"
- resolved "https://registry.npmjs.org/babel-plugin-jsx-dom-expressions/-/babel-plugin-jsx-dom-expressions-0.38.5.tgz"
- integrity sha512-JfjHYKOKGwoiOYQ56Oo8gbZPb9wNMpPuEEUhSCjMpnuHM9K21HFIUBm83TZPB40Av4caCIW4Tfjzpkp/MtFpMw==
- dependencies:
- "@babel/helper-module-imports" "7.18.6"
- "@babel/plugin-syntax-jsx" "^7.18.6"
- "@babel/types" "^7.20.7"
- html-entities "2.3.3"
- validate-html-nesting "^1.2.1"
-
-babel-preset-solid@^1.8.4:
- version "1.8.22"
- resolved "https://registry.npmjs.org/babel-preset-solid/-/babel-preset-solid-1.8.22.tgz"
- integrity sha512-nKwisb//lZsiRF2NErlRP64zVTJqa1OSZiDnSl0YbcTiCZoMt52CY2Pg+9fsYAPtjYMT7RHBmzU41pxK6hFOcg==
- dependencies:
- babel-plugin-jsx-dom-expressions "^0.38.5"
-
-browserslist@^4.23.1, "browserslist@>= 4.21.0":
- version "4.23.3"
- resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz"
- integrity sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==
- dependencies:
- caniuse-lite "^1.0.30001646"
- electron-to-chromium "^1.5.4"
- node-releases "^2.0.18"
- update-browserslist-db "^1.1.0"
-
-buffer-builder@^0.2.0:
- version "0.2.0"
- resolved "https://registry.npmjs.org/buffer-builder/-/buffer-builder-0.2.0.tgz"
- integrity sha512-7VPMEPuYznPSoR21NE1zvd2Xna6c/CloiZCfcMXR1Jny6PjX0N4Nsa38zcBFo/FMK+BlA+FLKbJCQ0i2yxp+Xg==
-
-caniuse-lite@^1.0.30001646:
- version "1.0.30001655"
- resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001655.tgz"
- integrity sha512-jRGVy3iSGO5Uutn2owlb5gR6qsGngTw9ZTb4ali9f3glshcNmJ2noam4Mo9zia5P9Dk3jNNydy7vQjuE5dQmfg==
-
-chalk@^2.4.2:
- version "2.4.2"
- resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz"
- integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
- dependencies:
- ansi-styles "^3.2.1"
- escape-string-regexp "^1.0.5"
- supports-color "^5.3.0"
-
-color-convert@^1.9.0:
- version "1.9.3"
- resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz"
- integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
- dependencies:
- color-name "1.1.3"
-
-color-name@1.1.3:
- version "1.1.3"
- resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz"
- integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==
-
-convert-source-map@^2.0.0:
- 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==
-
-csstype@^3.1.0:
- version "3.1.3"
- resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz"
- integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==
-
-debug@^4.1.0, debug@^4.3.1:
- version "4.3.6"
- resolved "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz"
- integrity sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==
- dependencies:
- ms "2.1.2"
-
-electron-to-chromium@^1.5.4:
- version "1.5.13"
- resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.13.tgz"
- integrity sha512-lbBcvtIJ4J6sS4tb5TLp1b4LyfCdMkwStzXPyAgVgTRAsep4bvrAGaBOP7ZJtQMNJpSQ9SqG4brWOroNaQtm7Q==
-
-esbuild@^0.21.3:
- version "0.21.5"
- resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz"
- integrity sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==
- optionalDependencies:
- "@esbuild/aix-ppc64" "0.21.5"
- "@esbuild/android-arm" "0.21.5"
- "@esbuild/android-arm64" "0.21.5"
- "@esbuild/android-x64" "0.21.5"
- "@esbuild/darwin-arm64" "0.21.5"
- "@esbuild/darwin-x64" "0.21.5"
- "@esbuild/freebsd-arm64" "0.21.5"
- "@esbuild/freebsd-x64" "0.21.5"
- "@esbuild/linux-arm" "0.21.5"
- "@esbuild/linux-arm64" "0.21.5"
- "@esbuild/linux-ia32" "0.21.5"
- "@esbuild/linux-loong64" "0.21.5"
- "@esbuild/linux-mips64el" "0.21.5"
- "@esbuild/linux-ppc64" "0.21.5"
- "@esbuild/linux-riscv64" "0.21.5"
- "@esbuild/linux-s390x" "0.21.5"
- "@esbuild/linux-x64" "0.21.5"
- "@esbuild/netbsd-x64" "0.21.5"
- "@esbuild/openbsd-x64" "0.21.5"
- "@esbuild/sunos-x64" "0.21.5"
- "@esbuild/win32-arm64" "0.21.5"
- "@esbuild/win32-ia32" "0.21.5"
- "@esbuild/win32-x64" "0.21.5"
-
-escalade@^3.1.2:
- version "3.2.0"
- resolved "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz"
- integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==
-
-escape-string-regexp@^1.0.5:
- version "1.0.5"
- resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz"
- integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==
-
-gensync@^1.0.0-beta.2:
- 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==
-
-globals@^11.1.0:
- version "11.12.0"
- resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz"
- integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
-
-has-flag@^3.0.0:
- version "3.0.0"
- resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz"
- integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==
-
-has-flag@^4.0.0:
- version "4.0.0"
- resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz"
- integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
-
-html-entities@2.3.3:
- version "2.3.3"
- resolved "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz"
- integrity sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==
-
-immutable@^4.0.0:
- version "4.3.7"
- resolved "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz"
- integrity sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==
-
-is-what@^4.1.8:
- version "4.1.16"
- resolved "https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz"
- integrity sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==
-
-js-tokens@^4.0.0:
- version "4.0.0"
- resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz"
- integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
-
-jsesc@^2.5.1:
- version "2.5.2"
- resolved "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz"
- integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==
-
-json5@^2.2.3:
- version "2.2.3"
- resolved "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz"
- integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==
-
-lru-cache@^5.1.1:
- version "5.1.1"
- resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz"
- integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==
- dependencies:
- yallist "^3.0.2"
-
-merge-anything@^5.1.7:
- version "5.1.7"
- resolved "https://registry.npmjs.org/merge-anything/-/merge-anything-5.1.7.tgz"
- integrity sha512-eRtbOb1N5iyH0tkQDAoQ4Ipsp/5qSR79Dzrz8hEPxRX10RWWR/iQXdoKmBSRCThY1Fh5EhISDtpSc93fpxUniQ==
- dependencies:
- is-what "^4.1.8"
-
-moment@^2.30.1:
- version "2.30.1"
- resolved "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz"
- integrity sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==
-
-ms@2.1.2:
- version "2.1.2"
- resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz"
- integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
-
-nanoid@^3.3.7:
- version "3.3.7"
- resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz"
- integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==
-
-node-releases@^2.0.18:
- version "2.0.18"
- resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz"
- integrity sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==
-
-picocolors@^1.0.0, picocolors@^1.0.1:
- version "1.0.1"
- resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz"
- integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==
-
-postcss@^8.4.41:
- version "8.4.42"
- resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.42.tgz"
- integrity sha512-hywKUQB9Ra4dR1mGhldy5Aj1X3MWDSIA1cEi+Uy0CjheLvP6Ual5RlwMCh8i/X121yEDLDIKBsrCQ8ba3FDMfQ==
- dependencies:
- nanoid "^3.3.7"
- picocolors "^1.0.1"
- source-map-js "^1.2.0"
-
-rollup@^4.20.0:
- version "4.21.2"
- resolved "https://registry.npmjs.org/rollup/-/rollup-4.21.2.tgz"
- integrity sha512-e3TapAgYf9xjdLvKQCkQTnbTKd4a6jwlpQSJJFokHGaX2IVjoEqkIIhiQfqsi0cdwlOD+tQGuOd5AJkc5RngBw==
- dependencies:
- "@types/estree" "1.0.5"
- optionalDependencies:
- "@rollup/rollup-android-arm-eabi" "4.21.2"
- "@rollup/rollup-android-arm64" "4.21.2"
- "@rollup/rollup-darwin-arm64" "4.21.2"
- "@rollup/rollup-darwin-x64" "4.21.2"
- "@rollup/rollup-linux-arm-gnueabihf" "4.21.2"
- "@rollup/rollup-linux-arm-musleabihf" "4.21.2"
- "@rollup/rollup-linux-arm64-gnu" "4.21.2"
- "@rollup/rollup-linux-arm64-musl" "4.21.2"
- "@rollup/rollup-linux-powerpc64le-gnu" "4.21.2"
- "@rollup/rollup-linux-riscv64-gnu" "4.21.2"
- "@rollup/rollup-linux-s390x-gnu" "4.21.2"
- "@rollup/rollup-linux-x64-gnu" "4.21.2"
- "@rollup/rollup-linux-x64-musl" "4.21.2"
- "@rollup/rollup-win32-arm64-msvc" "4.21.2"
- "@rollup/rollup-win32-ia32-msvc" "4.21.2"
- "@rollup/rollup-win32-x64-msvc" "4.21.2"
- fsevents "~2.3.2"
-
-rxjs@^7.4.0:
- version "7.8.1"
- resolved "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz"
- integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==
- dependencies:
- tslib "^2.1.0"
-
-sass-embedded-linux-musl-x64@1.78.0:
- version "1.78.0"
- resolved "https://registry.npmjs.org/sass-embedded-linux-musl-x64/-/sass-embedded-linux-musl-x64-1.78.0.tgz"
- integrity sha512-azdUcZZvZmtUBslIKr2/l4aQrTX7BvO96TD0GLdWz9vuXZrokYm09AJZEnb5j6Pk5I4Xr0yM6BG1Vgcbzqi5Zg==
-
-sass-embedded-linux-x64@1.78.0:
- version "1.78.0"
- resolved "https://registry.npmjs.org/sass-embedded-linux-x64/-/sass-embedded-linux-x64-1.78.0.tgz"
- integrity sha512-m997ThzpMwql4u6LzZCoHPIQkgK6bbLPLc7ydemo2Wusqzh6j8XAGxVT5oANp6s2Dmj+yh49pKDozal+tzEX9w==
-
-sass-embedded@*, sass-embedded@^1.78.0:
- version "1.78.0"
- resolved "https://registry.npmjs.org/sass-embedded/-/sass-embedded-1.78.0.tgz"
- integrity sha512-NR2kvhWVFABmBm0AqgFw9OweQycs0Qs+/teJ9Su+BUY7up+f8S5F/Zi+7QtAqJlewsQyUNfzm1vRuM+20lBwRQ==
- dependencies:
- "@bufbuild/protobuf" "^1.0.0"
- buffer-builder "^0.2.0"
- immutable "^4.0.0"
- rxjs "^7.4.0"
- supports-color "^8.1.1"
- varint "^6.0.0"
- optionalDependencies:
- sass-embedded-android-arm "1.78.0"
- sass-embedded-android-arm64 "1.78.0"
- sass-embedded-android-ia32 "1.78.0"
- sass-embedded-android-riscv64 "1.78.0"
- sass-embedded-android-x64 "1.78.0"
- sass-embedded-darwin-arm64 "1.78.0"
- sass-embedded-darwin-x64 "1.78.0"
- sass-embedded-linux-arm "1.78.0"
- sass-embedded-linux-arm64 "1.78.0"
- sass-embedded-linux-ia32 "1.78.0"
- sass-embedded-linux-musl-arm "1.78.0"
- sass-embedded-linux-musl-arm64 "1.78.0"
- sass-embedded-linux-musl-ia32 "1.78.0"
- sass-embedded-linux-musl-riscv64 "1.78.0"
- sass-embedded-linux-musl-x64 "1.78.0"
- sass-embedded-linux-riscv64 "1.78.0"
- sass-embedded-linux-x64 "1.78.0"
- sass-embedded-win32-arm64 "1.78.0"
- sass-embedded-win32-ia32 "1.78.0"
- sass-embedded-win32-x64 "1.78.0"
-
-semver@^6.3.1:
- version "6.3.1"
- resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz"
- integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
-
-seroval-plugins@^1.1.0:
- version "1.1.1"
- resolved "https://registry.npmjs.org/seroval-plugins/-/seroval-plugins-1.1.1.tgz"
- integrity sha512-qNSy1+nUj7hsCOon7AO4wdAIo9P0jrzAMp18XhiOzA6/uO5TKtP7ScozVJ8T293oRIvi5wyCHSM4TrJo/c/GJA==
-
-seroval@^1.0, seroval@^1.1.0:
- version "1.1.1"
- resolved "https://registry.npmjs.org/seroval/-/seroval-1.1.1.tgz"
- integrity sha512-rqEO6FZk8mv7Hyv4UCj3FD3b6Waqft605TLfsCe/BiaylRpyyMC0b+uA5TJKawX3KzMrdi3wsLbCaLplrQmBvQ==
-
-solid-js@^1.3, solid-js@^1.7.2, solid-js@^1.8.20:
- version "1.8.22"
- resolved "https://registry.npmjs.org/solid-js/-/solid-js-1.8.22.tgz"
- integrity sha512-VBzN5j+9Y4rqIKEnK301aBk+S7fvFSTs9ljg+YEdFxjNjH0hkjXPiQRcws9tE5fUzMznSS6KToL5hwMfHDgpLA==
- dependencies:
- csstype "^3.1.0"
- seroval "^1.1.0"
- seroval-plugins "^1.1.0"
-
-solid-refresh@^0.6.3:
- version "0.6.3"
- resolved "https://registry.npmjs.org/solid-refresh/-/solid-refresh-0.6.3.tgz"
- integrity sha512-F3aPsX6hVw9ttm5LYlth8Q15x6MlI/J3Dn+o3EQyRTtTxidepSTwAYdozt01/YA+7ObcciagGEyXIopGZzQtbA==
- dependencies:
- "@babel/generator" "^7.23.6"
- "@babel/helper-module-imports" "^7.22.15"
- "@babel/types" "^7.23.6"
-
-source-map-js@^1.2.0:
- version "1.2.0"
- resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz"
- integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==
-
-supports-color@^5.3.0:
- version "5.5.0"
- resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz"
- integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
- dependencies:
- has-flag "^3.0.0"
-
-supports-color@^8.1.1:
- version "8.1.1"
- resolved "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz"
- integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==
- dependencies:
- has-flag "^4.0.0"
-
-to-fast-properties@^2.0.0:
- 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==
-
-tslib@^2.1.0:
- version "2.7.0"
- resolved "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz"
- integrity sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==
-
-typescript@^5.5.3:
- version "5.5.4"
- resolved "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz"
- integrity sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==
-
-update-browserslist-db@^1.1.0:
- version "1.1.0"
- resolved "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz"
- integrity sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==
- dependencies:
- escalade "^3.1.2"
- picocolors "^1.0.1"
-
-validate-html-nesting@^1.2.1:
- version "1.2.2"
- resolved "https://registry.npmjs.org/validate-html-nesting/-/validate-html-nesting-1.2.2.tgz"
- integrity sha512-hGdgQozCsQJMyfK5urgFcWEqsSSrK63Awe0t/IMR0bZ0QMtnuaiHzThW81guu3qx9abLi99NEuiaN6P9gVYsNg==
-
-varint@^6.0.0:
- version "6.0.0"
- resolved "https://registry.npmjs.org/varint/-/varint-6.0.0.tgz"
- integrity sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==
-
-vite-plugin-solid@^2.10.2:
- version "2.10.2"
- resolved "https://registry.npmjs.org/vite-plugin-solid/-/vite-plugin-solid-2.10.2.tgz"
- integrity sha512-AOEtwMe2baBSXMXdo+BUwECC8IFHcKS6WQV/1NEd+Q7vHPap5fmIhLcAzr+DUJ04/KHx/1UBU0l1/GWP+rMAPQ==
- dependencies:
- "@babel/core" "^7.23.3"
- "@types/babel__core" "^7.20.4"
- babel-preset-solid "^1.8.4"
- merge-anything "^5.1.7"
- solid-refresh "^0.6.3"
- vitefu "^0.2.5"
-
-"vite@^3.0.0 || ^4.0.0 || ^5.0.0", vite@^5.4.1:
- version "5.4.2"
- resolved "https://registry.npmjs.org/vite/-/vite-5.4.2.tgz"
- integrity sha512-dDrQTRHp5C1fTFzcSaMxjk6vdpKvT+2/mIdE07Gw2ykehT49O0z/VHS3zZ8iV/Gh8BJJKHWOe5RjaNrW5xf/GA==
- dependencies:
- esbuild "^0.21.3"
- postcss "^8.4.41"
- rollup "^4.20.0"
- optionalDependencies:
- fsevents "~2.3.3"
-
-vitefu@^0.2.5:
- version "0.2.5"
- resolved "https://registry.npmjs.org/vitefu/-/vitefu-0.2.5.tgz"
- integrity sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==
-
-yallist@^3.0.2:
- version "3.1.1"
- resolved "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz"
- integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==